非常奇怪且严重的多线程不一致问题c#

发布于 2024-10-09 10:34:22 字数 1383 浏览 8 评论 0原文

我有一个非常简单的看门狗程序,有 2 个线程。一个线程正在更新一个长变量,另一个线程正在读取该变量。如果距离上次更新超过 X 秒,则会发出警报。问题是有时(或多或少每天发生一次)第二个线程读取变量的陈旧值。

有时它是 3 秒前的陈旧值(即第一个线程更新了 long 变量,但 3 秒后另一个线程没有获取新值)

我使用锁,以避免多线程缓存问题。我还尝试了 Volatile、Interlock、volatileRead 等,但没有任何帮助。该类通过 VB 6 程序通过 COM 启动。该程序非常简单,所以我认为这是 C# 中的一个错误(可能与 COM 相关)。这是程序:

你能帮忙吗?

public class WatchDog
{
    long lastDate = DateTime.Now.ToBinary();

    private object dateLock = new object();
    bool WatchdogActive = true;
    int WatchdogTimeoutAlert = 5;
    int WatchdogCheckInterval = 6000;

    private void WatchdogThread()
    {
        try
        {
            while (WatchdogActive)
            {
                lock (dateLock)
                {
                    DateTime lastHB = DateTime.FromBinary(lastDate);

                    if ((DateTime.Now.Subtract(lastHB).TotalSeconds > WatchdogTimeoutAlert))
                    {
                        Console.WriteLine(" last Date is " + lastDate);

                    }
                }
                Thread.Sleep(WatchdogCheckInterval);
            }
        }
        catch (Exception Ex)
        {
        }
    }

    private void OnHeartbeatArrive(long heartbeatTime)
    {
        lock (dateLock)
        {
            lastDate = heartbeatTime;
            Console.WriteLine(" Got Heartbeat lastDate " + lastDate);
        }
    }
}

I have a very simple watchdog program with 2 threads. One thread is updating a long variable and the other thread reads the variable. and alert if it was more than X seconds from the last update. The problem is that sometimes (happens once a day more or less) the second thread reads a stale value of the variable.

Sometimes it is stale value from 3 seconds ago (i.e. the first thread updated the long variable but after 3 seconds the other thread didn't get the new value)

I am using lock, in order to avoid multi thread caching problem. I also tried Volatile, Interlock, volatileRead etc but nothing helps. The class is initiated via VB 6 program via COM. The program is very simple, so i think that it is a bug in C# (maybe COM related). this is the program:

Can you help please?

public class WatchDog
{
    long lastDate = DateTime.Now.ToBinary();

    private object dateLock = new object();
    bool WatchdogActive = true;
    int WatchdogTimeoutAlert = 5;
    int WatchdogCheckInterval = 6000;

    private void WatchdogThread()
    {
        try
        {
            while (WatchdogActive)
            {
                lock (dateLock)
                {
                    DateTime lastHB = DateTime.FromBinary(lastDate);

                    if ((DateTime.Now.Subtract(lastHB).TotalSeconds > WatchdogTimeoutAlert))
                    {
                        Console.WriteLine(" last Date is " + lastDate);

                    }
                }
                Thread.Sleep(WatchdogCheckInterval);
            }
        }
        catch (Exception Ex)
        {
        }
    }

    private void OnHeartbeatArrive(long heartbeatTime)
    {
        lock (dateLock)
        {
            lastDate = heartbeatTime;
            Console.WriteLine(" Got Heartbeat lastDate " + lastDate);
        }
    }
}

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

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

发布评论

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

评论(2

层林尽染 2024-10-16 10:34:22
        while (WatchdogActive)

这是行不通的,WatchdogActive 没有被声明为易失性的。在发布版本中,变量很可能存储在 CPU 寄存器中,它永远不会看到其他线程对该变量进行的更新。换句话说,即使您将看门狗关闭,它仍然会处于活动状态。

您应该在这里使用 ManualResetEvent,它的 WaitOne(int) 方法会自动处理 Sleep() 并为您提供更快的线程终止作为奖励。

一些奇怪的不一致。您在 3 秒处引用失败,但仅检查 >= 5 秒。 Sleep() 比检查长,因此有可能错过失败。您似乎喜欢空的 catch 块,总是为代码在没有任何诊断的情况下无法工作提供了很好的机会。我猜我们没有查看真正的代码,这使得很难看到微妙的线程问题。请假设这不是 C# 中的错误。

        while (WatchdogActive)

That doesn't work, WatchdogActive isn't declared volatile. In the Release build the variable is very likely to get stored in a CPU register, it never sees the update that some other thread makes to the variable. In other words, the watch dog will still be active, even though you turned it off.

You should use a ManualResetEvent here, its WaitOne(int) method automatically takes care of the Sleep() and gives you a much quicker thread termination as a bonus.

Some strange inconsistencies. You quote a failure at 3 seconds but you only check for >= 5 seconds. The Sleep() is longer than the check, making it possible to miss failures. You seem to like empty catch blocks, always giving great opportunities for code failing to work without any diagnostic. I'm guessing we're not looking at the real code, that makes it difficult to see subtle threading problems. Do work from the assumption that this is not a bug in C#.

笑着哭最痛 2024-10-16 10:34:22

通常我使用 lock() 来锁定“左侧”的易失性对象,在这种情况下,使用

volatile object lastDate = DateTime.Now.ToBinary();
...
lock(lastDate){...}

为什么你传递“long”而不是 DateTime?

normally i use lock() to volatile object that is on 'left side' in this case, use

volatile object lastDate = DateTime.Now.ToBinary();
...
lock(lastDate){...}

And why u pass 'long' instead of DateTime?

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