Пытается заменить lock() на SpinWait.SpinUntil(), но это не работает

Давайте начнем с кода;

checkedUnlock является HashSet<ulong>

_hashsetLock это объект

lock (_hashsetLock)
    newMap = checkedUnlock.Add(uniqueId);

против

fun в инт

SpinWait.SpinUntil(() => Interlocked.CompareExchange(ref fun, 1, 0) == 1);
newMap = checkedUnlock.Add(uniqueId);
fun = 0;

мое понимание SpinWait в этом случае должен работать как lock() но есть еще элементы, добавленные в HashSetиногда это соответствует блокировке, иногда есть еще от 1 до 5 элементов, которые делают очевидным, что он не работает

мое понимание неверно?

редактировать

Я попробовал это, и, кажется, работает, мой тест показывает тот же номер, что и lock() до сих пор

SpinWait spin = new SpinWait();
while (Interlocked.CompareExchange(ref fun, 1, 0) == 1)
   spin.SpinOnce();

так почему бы это работать с этим, но не SpinWait.SpinUntil()?

редактировать № 2

небольшое полное приложение, чтобы увидеть

в этом коде SpinWait.SpinUntil когда-нибудь взорвется (добавление сгенерирует исключение), но когда это сработает, количество будет другим, поэтому мое ожидаемое поведение для этого неверно

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var list = new List<int>();
            var rnd = new Random(42);
            for (var i = 0; i < 1000000; ++i)
                list.Add(rnd.Next(500000));



            object _lock1 = new object();
            var hashset1 = new HashSet<int>();

            int _lock2 = 0;
            var hashset2 = new HashSet<int>();

            int _lock3 = 0;
            var hashset3 = new HashSet<int>();

            Parallel.ForEach(list, item =>

            {
                /******************/
                lock (_lock1)
                    hashset1.Add(item);
                /******************/

                /******************/
                SpinWait.SpinUntil(() => Interlocked.CompareExchange(ref _lock2, 1, 0) == 1);

                hashset2.Add(item);

                _lock2 = 0;
                /******************/

                /******************/
                SpinWait spin = new SpinWait();
                while (Interlocked.CompareExchange(ref _lock3, 1, 0) == 1)
                    spin.SpinOnce();

                hashset3.Add(item);

                _lock3 = 0;
                /******************/

            });


            Console.WriteLine("Lock: {0}", hashset1.Count);
            Console.WriteLine("SpinWaitUntil: {0}", hashset2.Count);
            Console.WriteLine("SpinWait: {0}", hashset3.Count);

            Console.ReadKey();
        }

    }
}

1 ответ

Решение

Условие, используемое в SpinWait.SpinUntil, неверно.

  1. Interlocked.CompareExchange возвращает исходное значение переменной.
  2. MSDN документы SpinWait.SpinUntil говорит, условие

Делегат должен выполняться снова и снова, пока он не вернет true.

Вы хотите вращаться, пока не произойдет переход 0 -> 1, поэтому условие должно быть

Interlocked.CompareExchange(ref fun, 1, 0) == 0

Последующие вызовы CompareExchange в других потоках приводят к 1, поэтому они будут ждать, пока fun флаг восстанавливается до 0 потоком "победителя".

Некоторые дальнейшие замечания:

  • fun = 0; должен работать на архитектуре x86, но я не уверен, что это везде правильно. Если вы используете Interlocked для доступа к полю, лучше всего использовать Interlocked для доступа к этому полю. Поэтому я предлагаю Interlocked.Exchange(ref fun, 0) вместо.
  • SpinWait редко является хорошим решением с точки зрения производительности, поскольку он не позволяет операционной системе переводить вращающийся поток в состояние ожидания. Он должен использоваться только для очень коротких ожиданий. ( Пример правильного использования). Простые блокировки (также известные как Monitor.Enter/Exit) или SemaphoreSlim будут делать вообще, или вы можете рассмотреть ReaderWriterLockSlim, если число операций чтения >> число операций записи.
Другие вопросы по тегам