Подскажите, пожалуйста, пример когда необходимо использовать класс Interlocked (желательно код). То есть не простые фразы из книг о необходимости и преимуществах использования этого класса, а конкретный примеры когда без Interlocked не работает, а с ним все хорошо.
Нужен этот класс тогда, когда есть несколько потоков и есть доступ к разделяемой переменной, которую они могут модифицировать.
Например ты запускаешь Parallel.ForEach и желаешь видеть кол-во выполненных итераций, что бы видеть некоторый прогресс. Поэтому за пределами цикла ты объявил переменную cnt.
Так вот, если в делегате ты будешь ее просто инкрементировать через ++ и выводить на консоль, то ты увидишь, что может быть бред из-за конкурентного доступа.
Для того, что бы это увидеть я сделал вот такой мини пример:
int cnt = 0;
var list = Enumerable.Range(0, 10000).ToList();
var options = new ParallelOptions() {MaxDegreeOfParallelism = 8};
Parallel.ForEach(list, options, (element) =>
{
var localCnt = Interlocked.Increment(ref cnt);
if(localCnt!= cnt)
Console.WriteLine("{0} {1} {2}",cnt, localCnt, Thread.CurrentThread.ManagedThreadId);
});
Как видно, мы в небольшом случае попадем в условие if, когда несколько потоков параллельно модифицировали переменную и та переменная, которую мы локально себе сохранили не равна тому, что в данный момент содержится в cnt.
Результат выводимый в консоль будет не стабильным, так как напрямую зависит от того, как ОС спланирует эти потоки. А может так случится, что звезды сойдутся и проблем не будет.
А если бы мы это делали через простой ++, то в LocalCnt мы рисковали получить то, чего не ожидали.
Чтение или запись логических значений отдельно , но "сравнение и обмен" выполняет как чтение, так и запись на один и тот же адрес, что означает, что целая транзакция не атомарная. Если несколько потоков могут записываться в это же место, вам нужно сделать всю транзакцию атомарной, используя класс Interlocked.
Пример:
Int32 val = 5;
Interlocked.CompareExchange(ref val, 90, 5);
Console.WriteLine(val);
Результат:
90
List<int> list = Enumerable.Range(1, 100).ToList();
int cnt = 0;
for (int i = 0; i < 10; i++)
{
cnt = 0;
Parallel.ForEach(list, (element) =>
{
for (int k = 0; k < 1000; k++)
{
cnt += element;
}
});
Console.Write(cnt);
Console.Write(" - ");
cnt = 0;
Parallel.ForEach(list, (element) =>
{
for (int k = 0; k < 1000; k++)
{
Interlocked.Add(ref cnt, element);
}
});
Console.WriteLine(cnt);
}
Вот такой код все таки в некоторых случаях показывает что без класса Interlocked сумма получается неправильная
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости