Зачем использовать SyncLocks in.NET для простых операций, когда блокируемый класс доступен?
Я занимался простой многопоточностью в VB.NET на некоторое время, и только что получил в свой первый большой многопоточный проект. Я всегда все делал с помощью оператора Synclock
, потому что не думал, что есть лучший способ.
Я только что узнал о классе Interlocked
- это выглядит так, как будто все это:
Private SomeInt as Integer
Private SomeInt_LockObject as New Object
Public Sub IntrementSomeInt
Synclock SomeInt_LockObject
SomeInt += 1
End Synclock
End Sub
Можно заменить одним утверждением:
Interlocked.Increment(SomeInt)
Это обрабатывает все блокировки внутри и изменяет номер. Это было бы намного проще, чем написание собственных блокировок для простых операций (более длительные или более сложные операции, очевидно, все еще нуждаются в собственной блокировке).
Есть ли причина, по которой я бы использовал свою собственную блокировку, используя выделенные объекты блокировки, когда я могу сделать то же самое, используя методы Interlocked
?
5 ответов:
Вы правы;
Однако бывают ситуации, когда нужно использоватьInterlocked
следует использовать здесь, и будет быстрее, чемSyncLock
.
Однако классInterlocked
не является хорошо известным.SyncLock
иInterlocked
не поможет.
Короткий ответ заключается в том, что использование блокировки
Monitor
(SyncLock
в VB иlock { }
В C#) не только гарантирует, что только один поток за один раз может получить доступ к переменной (или, в строгом смысле, только один поток за один раз может получить блокировку объекта блокировки), но и создает барьер памяти, необходимый для обеспечения того, чтобы чтение переменной не оптимизировалось.Если вы никогда просто не считываете значение переменной (другими словами, вся ваша работа выполняется через вызовы
Interlocked
), то ты будешь в порядке. Однако, если вам нужно иметь возможность выполнить обычное чтение переменной, то ситуация сложнее. Безблокировочные операции чтения / записи обычно выполняются в C# с помощью ключевого словаvolatile
. Это позволяет компилятору считывать значение переменной везде, где она используется, вместо того, чтобы оптимизировать любое из этих считываний в локальный кэш. К сожалению, нет никакого эквивалента в VB.NET так что вам придется использовать что-то другое.Принятый ответ на это вопрос должен предоставить дополнительную информацию о том, что вы можете сделать. Короче говоря, большинство людей используют
SyncLock
в VB.NET потому что это проще и менее сложно, чем логика, необходимая для этогоSyncLock
.
Однажды я прочитал очень хорошее объяснение так называемых неатомных и атомарных (в VB: взаимосвязанных) операций и попытаюсь подвести итог.
Normal "non-atomic" operations consist of several steps
- > другие потоки могут работать между этими стрепами
"Atomic" operations consist of one only one step
-> другие потоки не могут выполнять работу, пока обрабатываются атомарные операции, атомарные операции всегда обрабатываются как целое
Блокируемый класс представляет собой набор таких атомарных операций и поэтому по определению является threadsafe. Даже с несколькими потоками выполняя операции чтения и записи с одной и той же переменной, эти операции абсолютно потокобезопасны.
Тем не менее комбинация этих threadsafe команд может быть небезопасной, так как между атомарными операциями могут возникать условия гонки.Поэтому, если вы хотите, например, сравнить 2 переменные, а затем увеличить меньшую, это не является потокобезопасным, даже если отдельные операции для них самих (блокируются.сравните, сцепились.прирост). Здесь вам еще придется попользоваться синклокс.
Кроме этого ограничения нет никакой" скрытой плохой стороны " блокировки.
Один пример для условия гонки с a = 5:
Thread1: a+=1 Thread2: a+=2 --> supposed to be 8, but can be only 6 or 7, but can also be 6 or 7 depending on which thread wins the race
Вариант 1:
T1 step 1: read 5 T1 step 2: add 1 = 6 T1 step 3: write 6 T2 step 1: read 6 T2 step 2: add 2 = 8 T2 step 3: write 8 --> is 8 as supposed
Или Вариант 2:
T1 step 1: read 5 T2 step 1: read 5 T1 step 2: add 1 = 6 T2 step 2: add 2 = 7 T2 step 3: write 7 T1 step 3: write 6 --> is only 6
Или Вариант 3:
T1 step 1: read 5 T2 step 1: read 5 T1 step 2: add 1 = 6 T2 step 2: add 2 = 7 T1 step 3: write 6 T2 step 3: write 7 --> is only 7
С блокировкой.инкремент:
Вариант 1:
T1 step 1: read 5, add 1, write 6 T2 step 1: read 6, add 2, write 8
Или Вариант 2:
T2 step 1: read 5, add 2, write 7 T1 step 1: read 7, add 1, write 8
-> во всех случаях a = 8, как предполагается, threadsafe решение
Все вопросы, которые были размещены здесь, можно решить, применив это простой пример к сомнительному коду.
Надеюсь, это поможет другим людям, которые гуглят эту тему. Янис