Зачем использовать SyncLocks в.NET для простых операций, когда доступен класс Interlocked?
Некоторое время я занимался простой многопоточностью в 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
-> другие потоки не могут выполнять работу, пока атомарные операции обрабатываются, атомарные операции всегда обрабатываются как целое
Сцепленный класс представляет собой набор таких атомарных операций и, следовательно, по определению является потокобезопасным. Даже если несколько потоков выполняют операции чтения и записи для одной и той же переменной, эти операции абсолютно поточнобезопасны.
Тем не менее, комбинация этих потоковобезопасных команд может быть небезопасной, поскольку между атомарными операциями могут возникать условия гонки.
Поэтому, если вы хотите, например, сравнить 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, как и предполагалось, безопасное решение
Все вопросы, которые были размещены здесь, можно решить, применив этот простой пример к сомнительному коду.
Надеюсь, что это поможет другим людям, которые Goo gle эту тему. Janis
Interlocked
ограничивается простыми операциями над Integer, Long и Boolean и т.п.
Если вы хотите добавить элемент в общий список (например, T), вам все равно понадобится SynClock
,