Зачем использовать 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.