Является ли потокобезопасным оператор++? [дубликат]


этот вопрос уже есть ответ здесь:

  • Являются ли инкрементеры / декрементеры (var++, var--) и т. д. потокобезопасными? 2 ответы

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

Я читал один из удивительных ответов Эрика Липперта на то, что ++я делаю. Он говорит, что это то, что действительно происходит:

  1. X вычисляется для получения переменной
  2. значение переменной копируется во временную папку
  3. временное значение увеличивается для получения нового значения (не перезаписывая временное!)
  4. новое значение хранится в переменной
  5. результатом операции является новый значение

Это заставило меня задуматься, что если два потока, где вызов ++i? Если первый поток находится на Шаге 3, когда второй поток находится на Шаге 2. (Что означает, если второй поток копирует значение в расположение temp до того, как первый поток сохранит новое значение в переменной?)

Если это произойдет, то казалось бы, что оба потока будут только увеличиваться i один раз вместо двух. (Если только все это не находится в lock.)

3   51   2011-01-07 20:15:29

3 ответа:

как указывали другие ответы, нет, ++ не является "потокобезопасным".

что-то, что я думаю, поможет вам узнать о многопоточности и ее опасностях, - это начать очень точно понимать, что вы подразумеваете под "threadsafe", потому что разные люди имеют в виду разные вещи. По сути, аспект потокобезопасности, о котором вы беспокоитесь, заключается в том, является ли операция atomic или нет. "Атомарная" операция-это та, которая гарантированно не будет наполовину завершите, когда он прерывается другим потоком.

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

в C# практически ничто не гарантировано быть атомарным. Кратко:

  • чтение 32-битного integer или float
  • читать ссылка
  • написание 32-битного целого числа или float
  • пишу ссылку

гарантированы, что будут атомарны (см. спецификацию для точных деталей.)

в частности, чтение и запись 64-битный integer или float составляет не гарантированно будет атомной. Если вы скажете:

C.x = 0xDEADBEEF00000000;

в одном потоке, и

C.x = 0x000000000BADF00D;

на другой поток, то можно на третий поток:

Console.WriteLine(C.x);

пусть это выпишет 0xDEADBEEF0BADF00D, хотя логически переменная никогда не содержала это значение. Язык C# оставляет за собой право сделать запись в длинный эквивалент записи в два int, один за другим, и на практике некоторые чипы действительно реализуют его таким образом. Переключение потока после первой записи может привести к тому, что читатель прочитает что-то неожиданное.

длинный и короткий из него: не делитесь что-нибудь между двумя потоками без блокировки. Блокировки только медленно, когда они удовлетворены; если у вас есть проблемы с производительностью из-за спорных блокировок, то исправьте любой архитектурный недостаток, ведущий к спорным замкам. Если замков нет оспаривается и все еще слишком медленно, только тогда вы должны рассмотреть возможность перехода к опасным методам низкой блокировки.

общий метод низкой блокировки, чтобы использовать здесь, конечно, чтобы позвонить Threading.Interlocked.Increment, который делает приращение целого числа таким образом, чтобы гарантированно быть атомарным. (Обратите внимание, однако, что он по-прежнему не дает гарантий относительно таких вещей, как то, что происходит, если каждый из двух потоков выполняет взаимосвязанные приращения двух разных переменных в разное время, а другие потоки пытаются определить какое приращение произошло "первым". C# не гарантирует, что все потоки будут видеть один согласованный порядок событий.)

нет, это не так.

анализ, который вы придумали, совершенно правильный -++--) операторы уязвимы для условий гонки, если они не используются с соответствующей семантикой блокировки.

их трудно получить правильно, но, к счастью, BCL имеет готовое решение для этих конкретных случаев:

вы должны использовать Interlocked.Increment если вы хотите атомарная операция инкремента. Существует также Decrement и несколько других полезных атомарных операций, определенных на Interlocked класса.

увеличивает указанную переменную и сохраняет результат в виде атомарной операции.

нет, i++ выглядит компактным, но на самом деле это просто сокращение для i = i + 1 и в этой форме легче увидеть, что она включает в себя чтение и запись "я". Без блокировки он просто не является потокобезопасным.

2 другие аргументы :

  • ' ++ ' не определяется как потокобезопасный
  • существование Interlocked.Increment (ref int x)

потокобезопасность является редким и дорогим, он будет четко рекламироваться при наличии.