Зачем использовать 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
), тогда все будет в порядке. Однако, если вам нужно иметь возможность выполнять обычное чтение переменной, тогда ситуация сложнее. Lockless чтения / записи обычно выполняются в C# с помощью volatile
ключевое слово. Это указывает компилятору читать значение переменной везде, где она используется, а не оптимизировать любое из этих чтений в локальный кэш. К сожалению, эквивалента в VB.NET так что тебе придется использовать что-то другое.
принят ответ для этот вопрос должны предоставить некоторую информацию о том, что вы можете сделать. Короче говоря, большинство людей используют SyncLock
in VB.NET потому что это проще и менее сложно, чем логика, необходимая для этого без SyncLock
.
однажды я прочитал очень хорошее объяснение так называемых неатомных и атомарных (в VB: interlocked) операций и попытаюсь подвести итог.
Normal "non-atomic" operations consist of several steps
- > другие потоки могут работать между этими полосами
"Atomic" operations consist of one only one step
- > другие потоки не могут выполнять работу во время обработки атомарных операций, атомарные операции всегда обрабатываются как целое
блокируемый класс представляет собой набор таких атомарных операций и поэтому по определению является потокобезопасным. Даже с несколькими потоками, выполняющими операции чтения и записи в одной переменной, эти операции абсолютно потокобезопасны.
все еще комбинация этих threadsafe команд может быть небезопасной, так как условия гонки могут возникать между атомарными операциями.
поэтому, если вы хотите, например, сравнить 2 переменные, а затем увеличить меньшую, это не является потокобезопасным, даже если отдельные операции для них самих (заблокированы.сравнить, сцепить.прирост.) Здесь вы все еще должны использовать synclocks.
кроме этого ограничения нет "скрытой плохой стороны" блокировки.
один пример для racecondition с 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 решение
все вопросы, которые были размещены здесь, можно решить, применив этот простой пример к сомнительному коду.
надеюсь, это поможет другим людям, которые google эту тему. Янис!--9-->
Interlocked
ограничивается простыми операциями над целочисленными, длинными и булевыми и такими.
Если вы хотите добавить элемент в общий список (из T), например, вам все равно понадобится SynClock
.