'死锁'只有一个被锁定的物体?

amu*_*are 6 .net c# multithreading locking winforms

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

所以,我想我会简单地覆盖窗体的Dispose()方法,在锁定代码中将布尔值设置为true,并检查布尔值并在锁定代码中调用事件.

但是,每次关闭表单时程序都会完全冻结.

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

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);
}
Run Code Online (Sandbox Code Playgroud)

我希望这里的任何人都知道这段代码有什么问题.先感谢您!

shf*_*301 6

您没有考虑的是传递给的委托Invoke在UI线程上异步调用.调用Invoke将消息发布到表单消息队列,并在以后获取.

发生的事情不是:

UI Thread                   Background Thread
                            Call update()
                            take lock
                            Call Invoke()
Call update()             
                            release lock
Call Dispose()
take lock
release lock
Run Code Online (Sandbox Code Playgroud)

但反而:

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
Run Code Online (Sandbox Code Playgroud)

因此,后台线程可以在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);
}
Run Code Online (Sandbox Code Playgroud)

_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             
Run Code Online (Sandbox Code Playgroud)


ken*_*n2k 1

我真的会在这里简单地说。我不会实现棘手的线程安全代码,而是简单地捕获异常,并且如果失败则不执行任何操作。

假设它是一个ObjectDisposedException

try
{
    this.Invoke(Invoke(handler));
}
catch (ObjectDisposedException)
{
    // Won't do anything here as
    // the object is not in the good state (diposed when closed)
    // so we can't invoke.
}
Run Code Online (Sandbox Code Playgroud)

它更简单,也很直接。如果注释指定了捕获异常的原因,我认为这是可以的。