Пример необходимости Interlocked

120
16 июня 2019, 13:10

Подскажите, пожалуйста, пример когда необходимо использовать класс Interlocked (желательно код). То есть не простые фразы из книг о необходимости и преимуществах использования этого класса, а конкретный примеры когда без Interlocked не работает, а с ним все хорошо.

Answer 1

Нужен этот класс тогда, когда есть несколько потоков и есть доступ к разделяемой переменной, которую они могут модифицировать.

Например ты запускаешь 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 мы рисковали получить то, чего не ожидали.

Answer 2

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

Пример:

Int32 val = 5;
Interlocked.CompareExchange(ref val, 90, 5);
Console.WriteLine(val);

Результат: 90

Answer 3
        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 сумма получается неправильная

READ ALSO
как сделать запрос modbus

как сделать запрос modbus

Как сделать запрос используя это: github

106
Как исправить конфликт BringIntoView и e.Handled?

Как исправить конфликт BringIntoView и e.Handled?

Не работает автопрокрутка (вертикальная) при отключеном автосдвиге(горизонтальной прокрутки при выделении с длинным названием item) горизонтальной...

103
Что за оператор ^=

Что за оператор ^=

Привествую, недавно увидел такой оператор ^=, но так и не нашел что он делает, применялся он к int, знаю что ^ это ислючения, но ^= да еще и для intЧТо...

110
Ошибка при создании объекта

Ошибка при создании объекта

При создании Table Unit выдаёт Предполагаемый лог UnitTest https://yadisk/d/KnbKKhyEYZq6Hw При этом, если в методе CreateRows будет отсутствовать строка r[i] = new Row(this,...

88