Разрешение условий гонки на рекурсивном авторезервенте

В приведенном ниже коде у меня есть класс, который используется для управления синхронизацией, называется KernalRecursiveAutoResetEvent. У него есть метод Lock and Leave, который вызывает AutoResetEvent WaitOne() для блокировки и Set() для освобождения. Я настроил цикл, чтобы я мог вызвать метод, который имеет дело с общим ресурсом. В методе Incre ment я добавляю int в общий список. Условие гонки исчезает, потому что Leave вызывается быстрее, чем Lock. Есть ли лучший способ контролировать исполнение? Исключение InvalidOperation вызывается, потому что проверка условия, равен ли текущий поток принадлежащему потоку, выполняется непосредственно перед тем, как поток-владелец устанавливается с помощью метода Lock().

Какие-нибудь советы?

        class KernalRecursiveAutoResetEvent : IDisposable
            {
                private AutoResetEvent m_lock = new AutoResetEvent(false);
                private int m_owningThreadId = 0;
                private int m_recusionCount = 0;

                public void Lock()
                {
                    int currentThreadId = Thread.CurrentThread.ManagedThreadId;

                    if (m_owningThreadId == currentThreadId)
                    {
                        m_recusionCount++;
                        return;
                    }

                    m_lock.WaitOne();

                    m_owningThreadId = currentThreadId;
                    m_recusionCount = 1;
                }

                public void Leave()
                {
                    if (m_owningThreadId != Thread.CurrentThread.ManagedThreadId)
                        throw new InvalidOperationException();

                    if (--m_recusionCount == 0)
                    {
                        m_owningThreadId = 0;
                        m_lock.Set();
                    }
                }

                public void Dispose()
                {
                    m_lock.Close();
                }
            }

    using (var rare = new KernalRecursiveAutoResetEvent())
                {
                    for (int i = 0; i < iterations; i++)
                    {
                        var t = new Thread(a => Increment(ref i, rare));
                        t.Start();
                        rare.Lock();
                    }
                }

private static void Increment(ref int i, object _lock)
        {
            Increment(ref i);

            if (_lock is KernalRecursiveAutoResetEvent)
            {
                var m_lock = _lock as KernalRecursiveAutoResetEvent;
                m_lock.Leave();
            }
            else if (_lock is KernalModeMutexSimpleWaitLock)
            {
                var m_lock = _lock as KernalModeMutexSimpleWaitLock;
                m_lock.Leave();
            }
            else if (_lock is KernalModeSemaphoreSimpleWaitLock)
            {
                var m_lock = _lock as KernalModeSemaphoreSimpleWaitLock;
                m_lock.Leave();
            }
            else if (_lock is KernalModeSimpleWaitLock)
            {
                var m_lock = _lock as KernalModeSimpleWaitLock;
                m_lock.Leave();
            }
        }

private static void Increment(ref int i)
        {
            i++;
        }

1 ответ

Это излишне сложно. Вы должны просто использовать lock() вокруг кода, где вы добавляете новый int в список. Конечно, объект, который вы блокируете, должен быть общим для всех "потоков".

пример:

var lockObj = new object();

for (int i = 0; i < iterations; i++)
{
    // what do you mean `red i`, by the way? It's a value type.
    var t = new Thread(a => Increment(ref i, lockObj));
    t.Start();
}

внутри Increment:

lock (lockObj)
{
  someIntList.Add(i); // or whatever. Only one thread at a time can do this!
}

Конечно, ваш lockObj также может быть статичным.

Кроме того, не начинайте новые темы. использование Task вместо.

Другие вопросы по тегам