c# - 只有一个锁定对象的“死锁”?

标签 c# .net winforms multithreading locking

我在 C# 中遇到多线程问题。 我使用事件从另一个线程更新表单中的标签,为此我当然需要使用 Invoke() 命令。 那部分也工作正常。 但是,用户可以关闭表单,如果在不幸的时间发送事件,程序可能会在此处崩溃。

所以,我想我会简单地覆盖表单的 Dispose() 方法,在锁定代码中将 bool 值设置为 true,然后检查该 bool 值并在锁定代码中调用事件。

但是,每次我关闭表单时,程序都会完全卡住。

以下是代码中提到的部分:

private object dispose_lock = new object();
private bool _disposed = false;

private void update(object sender, EventArgs e)
{
    if (InvokeRequired)
    {
        EventHandler handler = new EventHandler(update);
        lock (dispose_lock)
        {
            if (_disposed) return;
            Invoke(handler); // this is where it crashes without using the lock
        }
        return;
    }

    label.Text = "blah";
}

protected override void Dispose(bool disposing)
{
    eventfullObject.OnUpdate -= update;
    lock (dispose_lock) // this is where it seems to freeze
    {
       _disposed = true; // this is never called
    }
    base.Dispose(disposing);
}

我希望这里的任何人都知道这段代码有什么问题。 提前致谢!

最佳答案

您没有考虑到传递给 Invoke 的委托(delegate)是在 UI 线程上异步调用的。调用 Invoke 将消息发布到表单消息队列,并在一段时间后被拾取。

发生的事情不是:

UI Thread                   Background Thread
                            Call update()
                            take lock
                            Call Invoke()
Call update()             
                            release lock
Call Dispose()
take lock
release lock

而是:

UI Thread                   Background Thread
                            Call update()
                              take lock
                               Call Invoke()
                               block until UI Thread processes the message
Process messages
...
Dispose() 
   wait for lock ****** Deadlock! *****
...
Call update()             
                            release lock

因此,后台线程可以在 UI 线程尝试运行 Dispose 时保持锁定状态

解决方案比您尝试的要简单得多。由于 Invoke 是异步发布的,因此不需要锁。

private bool _disposed = false;

private void update(object sender, EventArgs e)
{
    if (InvokeRequired)
    {
        EventHandler handler = new EventHandler(update);
        Invoke(handler);
        return;
    }

    if (_disposed) return;

    label.Text = "blah";
}

protected override void Dispose(bool disposing)
{
    eventfullObject.OnUpdate -= update;
    _disposed = true; // this is never called
    base.Dispose(disposing);
}

_disposed 标志仅在 UI 线程上读取或写入,因此不需要锁定。现在你调用堆栈看起来像:

UI Thread                   Background Thread
                            Call update()
                               take lock
                               Call Invoke()
                               block until UI Thread processes the message
Process messages
...
Dispose() 
  _disposed = true;
...

Call update()
  _disposed is true so do nothing             

关于c# - 只有一个锁定对象的“死锁”?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9045938/

相关文章:

c# - 循环遍历 Awesomium JSObject

c# - 使用 lastIndexOf() 时出现 ArgumentOutOfRangeException

C# - Xamarin Forms 音频播放器在使用 5 或 6 次后停止工作

c# - C# 闭包中的 Lambda 表达式吗?

c# - 对不同长度的二维数组进行排序

C#:Windows 窗体:什么会导致 Invalidate() 不重绘?

winforms - 如何使用winforms创建选项卡式文档环境?

c# - 服务器和客户端之间的双向通信

c# - 调试执行路径中缺少 msvcr100d.dll 和 msvcp100d.dll

.net - 将 Selenium 与 Powershell 结合使用 - 清除输入字段