Пока я читал о механизме блокировки ReaderWriterLockSlim
, был этот парень, который предположил, что функции Interlock
могут использоваться для более тонкой блокировки
Кроме того, я нашел здесь еще один ответ от Марка:
... Writes вносят изменения в клонированную копию, а затем используют Interlocked.CompareExchange для замены ссылки (повторное применение их изменения, если другой поток мутировал ссылку в промежутке времени).
Ну, В настоящее время все, что я знаю об объекте Interlocked
, заключается в том, что он используется (в многопоточной среде) для выполнения атомных additions
, compare
, compareExchange
операций compareExchange
. (и я знаю, как его использовать)
Но (и вот мой вопрос) -
Вопрос
Как я могу использовать его в качестве блокировки? (пример кода будет высоко оценен)
Для простоты я вставляю этот код (который не является потокобезопасным - если Go
был вызван двумя потоками одновременно, можно было бы получить ошибку деления на нуль):
class ThreadUnsafe
{
static int _val1 = 1, _val2 = 1;
static void Go()
{
if (_val2 != 0) Console.WriteLine (_val1 / _val2);
_val2 = 0;
}
}
Как я могу использовать блокировку для замены блокировки (которая бы решила проблему)?
Interlocked
используется для реализации блокирующих алгоритмов и структур данных. Следовательно, это не "более тонкая блокировка" или даже блокировка вообще. Он позволяет выполнять мелкие и четко определенные операции в многопоточной среде: например, если вы хотите, чтобы два потока увеличивали одну и ту же переменную, вы можете использовать Interlocked
для этого, вместо того чтобы приобретать супертяжелый замок и использовать "обычный увеличивает".
Тем не менее, существует много и много вещей, которые вы не можете сделать с помощью Interlocked
которую вы можете делать под обычной блокировкой. Например, все, что связано с модификацией более чем одной переменной, как правило, не может быть выполнено с помощью Interlocked
, поэтому вы не сможете использовать ее для своего примера.
Interlocked
может помочь разработчикам реализовать механизм блокировки, хотя вы можете также использовать встроенные. Для большинства шлюзов требуется поддержка ядра для прерывания заблокированного потока до тех пор, пока блокировка не станет доступной. Таким образом, единственный вид блокировки, который вы можете реализовать с помощью Interlocked
одиночку, - это прямая блокировка: блокировка, которую потоки постоянно пытаются получить, пока она не будет работать.
class InterlockedLock
{
private int locked;
public void Lock()
{
while (Interlocked.CompareExchange(ref locked, 1, 0) != 0)
continue; // spin
}
public void Unlock()
{
locked = 0;
}
}
В этом примере locked
изначально равна нулю. Когда поток пытается получить его в первый раз, locked
становится 1, а последующие потоки пытаются Lock
это будет вращаться, пока кто - нибудь не требуют Unlock
, чтобы сделать locked
снова 0.
Блокировка с Interlocked
выполняется путем замены нового значения на старое значение только в том случае, если старое значение не изменилось. Если мы попробуем еще раз, пока это не произойдет (вращение).
Это можно использовать для замены мелкозернистых замков, которые обновляют только одно значение или реализуют легкий механизм блокировки, такой как SpinLock
.
Пример:
private int _value;
int oldValue;
int originalValue;
do
{
oldValue = _value;
originalValue = Interlocked.CompareExchange(ref _value, newValue, oldValue);
}
while (originalValue != oldValue);
Вместо:
lock(_lock)
{
_value = newValue;
}
volatile
гарантировало, что запись вlocked
не будет переупорядочена и завершится раньше, чем какая-либо запись в критическом разделе.