【问题标题】:very strange and severe multithread inconsistency problem c#非常奇怪和严重的多线程不一致问题c#
【发布时间】:2011-05-31 01:29:48
【问题描述】:

我有一个非常简单的带有 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);
        }
    }
}

【问题讨论】:

  • 您可能需要提供一些关于如何调用OnHeartbeatArrive() 的信息,特别是如何确定传递给它的heartbeatTime
  • 要清楚,发布的代码是否与 WriteLine() 语句一起证明了问题?
  • @michael 当我通过 TCP 从服务器收到消息时,我使用 DateTime.Now.ToBinary() 调用 OnHeartbeatArrive。请注意,我打印相同的变量。我看到它从更新线程中更改了它的值,但我在读取线程中看到了一个旧值。
  • @Henk:是的。我使用控制台 writeLine 在更新后和读取中查看变量的值,然后我注意到读取器线程甚至在 3 秒内读取了一个旧值!在更新线程更新值并打印之后。
  • 您使用的是什么 IDE。您是从 IDE 启动程序吗?

标签: c# .net multithreading volatile interlocked


【解决方案1】:
        while (WatchdogActive)

这不起作用,WatchdogActive 没有被声明为volatile。在 Release 构建中,变量很可能存储在 CPU 寄存器中,它永远不会看到其他线程对变量进行的更新。换句话说,即使您将其关闭,看门狗仍将处于活动状态。

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

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

【讨论】:

  • 当然可以。阅读器线程确实每 5 秒检查一次。但流程如下:更新线程在时间 X -2 和 X 更新值。读取器线程在 X + 3 唤醒但没有获得最新的值。它得到 X - 2 的值。
  • 当然可以。阅读器线程确实每 5 秒检查一次。但流程如下:更新线程在时间 X -2 和 X 更新值。读取器线程在 X + 3 唤醒但没有获得最新的值。它得到 X - 2 的值。代码几乎与真实代码相同,因为我说它非常简单。不确定您的意思是 WatchdogActive 不是易变的。在大多数情况下,它工作正常,但它每天发生一次。我很高兴知道这不是与 c# com 相关的错误,但我的想法已经不多了。
  • 你怎么知道的?你整天盯着控制台窗口看 X 吗?或者这实际上被记录了吗?记录器有自己的锁定。使代码 sn-p 成为真实代码的精确副本。
  • 我一直在打印,但我不会整天看 :) 。触发器是我从看门狗那里得到的警报。当我收到警报时,我会看到所有打印件(所有更新),并且我会看到阅读器线程的打印件。是的,我正在使用记录器,但我看不出它如何解释上述内容。记录器时间与 DateTime.Now 完全相关。它也发生在 3 秒后,因此不太可能是记录器的问题。
【解决方案2】:

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

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

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

【讨论】:

  • 试试看:“volatile 对 long 类型无效...”
  • 我尝试了所有选项。我尝试使用 volatile + lock,我尝试使用 DateTime,我尝试使用 VolatileRead 读写,我尝试使用 Interlock 读写。没有任何效果。其实我是从 DateTime 开始的,我把它改成 long 只是为了验证它与 DateTime 问题无关
  • 也许使用 Stack。检查,使用 .Peek() 并且不要忘记清除堆栈以释放资源
猜你喜欢
  • 2016-08-03
  • 1970-01-01
  • 2021-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-07
相关资源
最近更新 更多