Запись на общий ресурс из 2 разных потоков без использования механизма синхронизации - PullRequest
1 голос
/ 18 июля 2011

У меня есть следующий код (написанный на c #, но его можно легко перевести на ваш предпочитаемый язык ...)

class Program
{
    static int sharedState = 0;

    static void Main(string[] args)
    {
        Thread t1 = new Thread(UpdateState);
        Thread t2 = new Thread(UpdateState);
        t1.Start();
        t2.Start();

        t1.Join();
        t2.Join();
        Console.WriteLine("sharedState value is {0}.", sharedState);
    }

    static void UpdateState()
    {
        for (int i = 0; i < 10; i++)
            sharedState++;
    }
}

Как вы можете догадаться, этот код создает два рабочих потока, которые увеличиваютзначение общего состояния по одному на 10 раз.Код не имеет никакого механизма синхронизации (такого как мьютекс, монитор или любой другой ...) при доступе и записи общего значения «sharedState», и основной поток ожидает, когда два рабочих закончат свою работу (Join).Может ли кто-нибудь объяснить мне, в чем проблема с этим кодом, и возможно ли, что в конце значение 'sharedState' будет равно 2?(моему другу был задан этот вопрос, и они сказали ему, что это возможно, и мы оба не можем понять, как ...) Кстати, когда я запускаю этот код, я каждый раз получаю 20 ...

Ответы [ 4 ]

3 голосов
/ 18 июля 2011

Потоки не живут достаточно долго. Вероятность того, что они будут работать одновременно, мала при таком коротком цикле, особенно на многоядерном компьютере. К тому времени, когда вы запускаете 2-й поток, 1-й поток уже завершен. Поскольку это занимает лишь долю микросекунды, гораздо меньше, чем требуется для запуска потока. Пусть они пройдут как минимум десять миллионов раз, чтобы увеличить шансы и увидеть гонку. Выходные данные на моей машине в сборке Release:

значение sharedState равно 13952221.

С разными значениями каждый раз, когда я запускаю его.

2 голосов
/ 18 июля 2011

Это не потокобезопасно, потому что

sharedState++;

интерпретируется как:

  • Передать значение из статического свойства в стек оценки
  • Увеличение значения в стеке оценки
  • Заменить значение в статическом свойстве значением из стека оценки

Теперь, если эта операция не является атомарной / синхронизированной, у вас может быть больше потоков, одновременно принимающих значение из статического свойства, но будет сохраняться только модификация из последнего потока.

Если вы хотите увеличить потокобезопасность, вы должны использовать Interlocked.Increment

0 голосов
/ 18 июля 2011
  1. Оба потока читают переменную и получают значение 0.
  2. Потоку 1 удается увеличить значение переменной в 9 раз.
  3. Потоку 2 удается увеличить значение переменной один раз.Но раньше он думал, что было 0, поэтому его новое значение равно 1.
  4. Оба потока читают переменную и получают значение 1.
  5. Потоку 2 удается увеличить значение переменной в 9 раз.
  6. Потоку 1 удается увеличить значение переменной один раз.Но раньше он думал, что это был 1, поэтому его новое значение равно 2.

Конечно, на практике это крайне маловероятно!

0 голосов
/ 18 июля 2011

Проблема действительно есть. Но это будет только в том случае, если вы будете запускать цикл очень-очень часто:

Предположим, что состояние 1
поток читает состояние и получает 1
поток а приостановлен ОС, а б запущен
поток b читает состояние и получает 1
поток b вычисляет 1 + 1 и записывает 2
поток b читает 2
поток b вычисляет 2 + 1 и записывает 3
...
поток вычисляет 1 + 1 и записывает 2
3 шага при выполнении и состояние изменилось с 1 на 2

Но вы никогда не получите результат, который будет ниже вашего нижнего граничного условия. Поэтому, если вы начнете с 0 и каждый поток увеличится в 10 раз, вы всегда получите 10 или выше, независимо от того, что произойдет.

Для сныхронизации: Interlocked.Increment (ссылка, что угодно) твой друг. Это реализовано с использованием префикса блокировки в ассемблере. Это наиболее эффективный способ безопасного увеличения значения. Несколько более сложные операции могут быть выполнены с помощью CompareEcxchange. Но блокировка (что-то) тоже очень эффективна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...