在C#中实现同步算法

时间:2010-01-01 20:08:36

标签: c# .net multithreading synchronization thread-safety

我尝试在C#中实现同步算法,但没有成功。

为什么以下代码线程不安全?

using System;
using System.Threading;

namespace SoftwareLockTest
{
    class Program
    {
        private static volatile bool _isLocked1 = false;
        private static volatile bool _isLocked2 = false;
        private static volatile int _count = 0;

        static void Main(string[] args)
        {
            Thread thread2 = new Thread(Thread2Work);
            thread2.Start();

            Thread1Work();
        }

        public static void Thread1Work()
        {
            while (true)
            {
                _isLocked1 = true;

                while (_isLocked2)
                {
                    _isLocked1 = false;
                    while (_isLocked2) ;
                    _isLocked1 = true;
                }

                CriticalSection();
                _isLocked1 = false;
            }
        }

        public static void Thread2Work()
        {
            while (true)
            {
                _isLocked2 = true;

                while (_isLocked1)
                {
                    _isLocked2 = false;
                    while (_isLocked1) ;
                    _isLocked2 = true;
                }

                CriticalSection();
                _isLocked2 = false;
            }
        }

        private static void CriticalSection()
        {
            if (_count != 0)
            {
                Console.WriteLine("NOT THREAD SAFE 1");
                _count = 0;
            }

            _count++;

            if (_count != 1)
            {
                Console.WriteLine("NOT THREAD SAFE 2");
            }

            _count--;

            if (_count != 0)
            {
                Console.WriteLine("NOT THREAD SAFE 3");
            }
        }
    }
}

6 个答案:

答案 0 :(得分:6)

问题是读取后写入可能会重新排序(即使使用“volatile”)。你需要调用Thread.MemoryBarrier();在四个“while(_isLockedX)”循环前面。

请阅读http://www.albahari.com/threading/part4.aspx以获取有关内存障碍和易失性的解释。

对于任何真实的项目,请选择现有的锁定实现,而不是尝试自己创建。

答案 1 :(得分:5)

您正在尝试实施Dekker's algorithm。不幸的是,当硬件设计师和软件工程师仍然相互交谈时,他生活在更简单的时代。 PC业务供应商之间的激烈竞争,强调速度和核心,对Dekker先生的聪明才智造成了毁灭性的打击。有点高兴,我必须已经对该算法进行了数十次审查,并且从未头疼过。

嗯,这有点儿。查看该Wikipedia文章中的“注释”,了解该算法不再适用的原因。你有很多可行的替代方案。关键的一点是,你发现的有关5年以上并发性的文献不再具有相关性。

答案 2 :(得分:2)

嗯,目前还不完全清楚你试图用锁定标志做什么,并且通过线程理解代码是严重,但是没有lock / { {1}},我希望看到增量(Monitor)/减量(Interlocked)/ test(.Increment)有很多.Decrement。仅仅因为它是.CompareExchange并不意味着在执行volatile / ++时两个线程不会被绊倒。

但坦率地说,我只会使用--,除非你有充分的理由不这样做。你想保持简单 - “显然没有错误”,而不是“没有明显的错误”。

答案 3 :(得分:2)

我仍然试图解决这个问题,显然通常你应该确实使用锁...但我怀疑问题是volatile可能并不意味着什么你认为应该。

我希望您认为这意味着“当我写入此变量时,立即使该写入可见;当我从此变量读取时,读取一个绝对最新的值”。 It doesn't mean quite that(尽管MSDN说的是我自己的线程教程;当我对它有更好的处理时我需要更新它。)

我会看看我是否能确切地知道发生了什么,但看起来确实很奇怪。我想我明白你的代码正在尝试做什么,而且在做出关于波动性的“简单”假设时我还没有设法打破它......(我已经重现了这个问题,这总是有帮助的)

答案 4 :(得分:2)

有趣的是,几个小时前同样的查询在codeguru上了......这是我的回复,改编自那里,但是高位是你需要一个内存栅栏才能让这段代码正常工作。

您的代码的问题在于它依赖于您的系统顺序一致。 x86系统不是SC。它们就是所谓的处理器一致性。

它非常微妙,所以让我们简化你的代码:

thread1:

WRITE _isLocked1, 1
READ _isLocked2
(skip the while loop)
critical section
WRITE _isLocked1, 0

thread2:

WRITE _isLocked2, 1
READ _isLocked1
(skip the while loop)
critical section
WRITE _isLocked2, 0

处理器一致性仅表示thread1按照执行顺序观察线程2完成的写入(因此写入1然后写入0)。相反,线程2观察线程1的写入。什么是咬你的是处理器一致性很少说明thread1的写入如何与thread2的写入交错,除了依赖操作的因果关系被保持(这对你的例子来说并不重要)。

AMD文档第2卷第7.2节有一套很好的例子可以帮助你看到它并引用Dekker的算法以及为什么它需要x86系统上的内存栅栏。

答案 5 :(得分:0)

为什么不简单地使用lock

lock (_anyInstantiatedObject)
{
    CriticalSection();
}

这样你依靠操作系统来注意没有其他线程同时进入关键部分。