Зачем использовать SyncLocks в .NET для простых операций, когда доступен класс Interlocked? - PullRequest
9 голосов
/ 29 марта 2010

Некоторое время я занимался простой многопоточностью в 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 ]

9 голосов
/ 29 марта 2010

Вы правы; Interlocked должен использоваться здесь, и будет быстрее, чем SyncLock.
Однако класс Interlocked недостаточно известен.

Однако есть ситуации, когда вам нужно использовать SyncLock и Interlocked не поможет.

5 голосов
/ 29 марта 2010

Это потому, что никто не знает об этом. Распространите слово!

4 голосов
/ 29 марта 2010

Короткий ответ: потому что использование блокировки Monitor (SyncLock в VB и lock { } в C #) не только гарантирует, что только один поток за раз может получить доступ к переменной (или, в строгом смысле, только один поток за раз может получить блокировку для объекта блокировки), но он также создает барьер памяти, необходимый для обеспечения того, чтобы чтение переменной не оптимизировалось.

Если вы просто не читаете значение переменной (иными словами, вся ваша работа выполняется через вызовы Interlocked), то все будет в порядке. Однако, если вам необходимо выполнить обычное чтение переменной, ситуация более сложная. Чтение / запись без блокировки обычно выполняется в C # с помощью ключевого слова volatile. Это указывает компилятору читать значение переменной везде, где она используется, а не оптимизировать любое из этих чтений в локальный кеш. К сожалению, в VB.NET нет аналога, поэтому вам придется использовать что-то другое.

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

2 голосов
/ 07 ноября 2012

Однажды я прочитал очень хорошее объяснение о так называемых неатомарных и атомарных (в VB: взаимосвязанных) операциях и постараюсь подвести итог.

Normal "non-atomic" operations consist of several steps 

-> между этими стропами могут работать другие потоки

"Atomic" operations consist of one only one step 

-> другие потоки не могут выполнять работу, пока атомарные операции обрабатываются, атомарные операции всегда обрабатываются как единое целое

Класс блокировки - это набор таких атомарных операций, поэтому он по определению безопасен для потоков. Даже если несколько потоков выполняют операции чтения и записи для одной и той же переменной, эти операции абсолютно поточнобезопасны.

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

Так что, если вы хотите, например, сравнить 2 переменные, а затем увеличить меньшую, это не является потокобезопасным, хотя отдельные операции для них выполняются (interlocked.compare, interlocked.increment). Здесь вам еще нужно использовать синхронизаторы.

Кроме этого ограничения нет "скрытой плохой стороны" блокировки.

Один пример для условия гонки с = 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, как предполагается, многопоточное решение

Все вопросы, которые были размещены здесь, можно решить, применив этот простой пример к сомнительному коду.

Надеюсь, это поможет другим людям, которые погуглили эту тему. Janis

1 голос
/ 29 марта 2010

Interlocked ограничивается простыми операциями над Integer, Long и Boolean и т.п.

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

...