C#实现同步算法

发布于 2024-08-16 08:57:47 字数 1821 浏览 3 评论 0原文

我尝试用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");
            }
        }
    }
}

I trying to implement synchronization algorithm in C#, without success.

Why isn't the following code thread safe?

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");
            }
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

書生途 2024-08-23 08:57:47

问题是先写后读可能会被重新排序(即使使用“易失性”)。您需要调用 Thread.MemoryBarrier();在四个“while (_isLockedX)”循环前面。

请阅读 http://www.albahari.com/threading/part4.aspx 了解内存屏障和易失性的解释。

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

The problem is that a write followed by a read may be reordered (even with "volatile"). You need to call Thread.MemoryBarrier(); in front of the four "while (_isLockedX)" loops.

Please read http://www.albahari.com/threading/part4.aspx for an explanation of memory barriers and volatile.

For any real project, please prefer existing lock implementations instead of trying to make your own.

放肆 2024-08-23 08:57:47

您正在尝试实施Dekker 算法。不幸的是,他生活在更简单的时代,当时硬件设计师和软件工程师仍在互相交谈。 PC 行业供应商之间的残酷竞争强调速度和核心,这给德克先生的独创性带来了厄运。有点高兴,我一定已经复习了这个算法几十次了,而且从来没有不头痛过。

嗯,这有点元。查看该维基百科文章中的“注释”,了解该算法不再起作用的原因。你有很多可行的替代方案。关键的一点是,您找到的任何有关并发性的文献超过 5 年的历史都不再相关了。

You are trying to implement Dekker's algorithm. Unfortunately, he lived in simpler times, back when hardware designers and software engineers were still talking to each other. The cut-throat competition between vendors in the PC business, emphasizing speed and cores, spelled doom to Mr. Dekker's ingenuity. Kinda glad, I must have reviewed that algorithm dozens of times and never not got a headache.

Well, that's kinda meta. Review the "Note" in that Wikipedia article to see why the algorithm doesn't work anymore. You got plenty of alternatives offered that do work. Key point is that what ever literature you find about concurrency that's over 5 years old is no longer relevant.

歌入人心 2024-08-23 08:57:47

好吧,目前还不完全清楚您要尝试使用锁定标志做什么,并且理解代码对于线程来说是关键,但在没有锁定 / < code>Monitor,我希望看到很多用于增量 (.Increment) / 减量 (.Decrement) 的 Interlocked ) / 测试 (.CompareExchange)。仅仅因为它是易失性并不意味着两个线程在执行++/--时不会被绊倒。

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

Well, it isn't entirely clear what you are trying to do with the lock flags, and understanding the code is critical with threading, but in the absence of lock / Monitor, I would expect to see a lot of Interlocked for the increment (.Increment) / decrement (.Decrement) / test (.CompareExchange). Just because it is volatile doesn't mean that two threads can't get tripped up when doing ++/--.

But frankly, I would just use a lock unless you have a good reason not to. You want to keep it simple - "obviously no bugs", rather than "no obvious bugs".

世界如花海般美丽 2024-08-23 08:57:47

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

我希望您认为这意味着“当我写入此变量时,使写入立即可见;当我从此变量读取时,读取绝对最新的值”。 这并不完全是这个意思(尽管 MSDN 是这么说的)事实上,我自己的线程教程;当我更好地处理它时,我需要更新它)。

我会看看我是否能准确地弄清楚发生了什么,但它看起来确实很奇怪。我想我理解你的代码想要做什么,并且在对波动性做出“简单”假设时我还没有设法打破它......(并且我已经重现了这个问题,这总是有帮助的)

I'm still trying to work this out, and obviously normally you should indeed use locks... but I suspect the problem is that volatile probably doesn't mean exactly what you think it should.

I expect you think it means "when I write to this variable, make that write visible immediately; when I read from this variable, read an absolutely up-to-date value". It doesn't mean quite that (despite what MSDN says and indeed my own threading tutorial; I need to update that when I've got a better handle on it).

I'll see if I can work out exactly what's going on, but it does look odd. I think I understand what your code is trying to do, and I haven't managed to break it when making the "simple" assumption about volatility yet... (and I've reproduced the problem, which is always helpful)

未蓝澄海的烟 2024-08-23 08:57:47

有趣的是,几个小时前在 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

处理器一致性仅表示线程 1 按照执行顺序观察线程 2 完成的写入(因此先写入 1,然后写入 0)。相反,线程 2 对线程 1 写入的观察。令您困扰的是处理器一致性几乎没有说明线程 1 的写入如何与线程 2 的写入交错,除了保持依赖操作的因果关系(这对您的示例并不重要)。

AMD 文档第 2 卷第 7.2 节提供了一组很好的示例,可以帮助您了解它并引用 Dekker 算法以及为什么它在 x86 系统上需要内存栅栏。

Amusingly, this same query was up on codeguru a few hours ago... here's my reply, adapted from there, but the high bit is you need a memory fence for this code to work right.

The problem with your code is that it relies on your system to be sequentially consistent. x86 systems are not SC. They are what is known as processor consistent.

Its very subtle, so lets simplify your code just a tad:

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

Processor consistency only says that thread1 observes the writes done by thread 2 in the order they are performed (so write 1 then write 0). Conversely thread 2's observation of thread 1's writes. What is biting you is processor consistency says little about how the writes from thread1 are interleaved with the writes from thread2, except that causality of dependent operations are maintained (which isn't important to your example).

The AMD documentation Volume 2, section 7.2 has a nice set of examples on this that will help you see it and references Dekker's algorithm and why it needs a memory fence on x86 systems.

长不大的小祸害 2024-08-23 08:57:47

为什么不简单地使用lock

lock (_anyInstantiatedObject)
{
    CriticalSection();
}

这样,您就可以依靠操作系统来确保没有其他线程同时进入临界区。

Why don't you simply use lock?

lock (_anyInstantiatedObject)
{
    CriticalSection();
}

That way you rely on OS to take care that no other thread enters the critical section at the same time.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文