解决递归autoresertevent上的竞争条件

时间:2016-11-20 21:11:48

标签: c# multithreading autoresetevent

在下面的代码中,我有一个用于控制同步的类,名为KernalRecursiveAutoResetEvent。这有一个Lock和Leave方法,它调用AutoResetEvent WaitOne()进行锁定,调用Set()进行释放。我已经设置了一个循环,所以我可以调用一个处理共享资源的方法。在Increment方法中,我将一个int添加到共享列表中。种族分歧是因为离开被称为比洛克更快。有没有更好的方法来控制执行?抛出了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 个答案:

答案 0 :(得分:1)

这不必要地复杂化。您应该在将新int添加到列表的代码周围使用lock()。当然,你锁定的对象应该在所有线程之间是通用的。

示例:

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