0

说明(.Net framework 3.5/Windows7/VS12): 1)Operation是一种可以异步运行的类型。它通过名为 Executed 的事件通知其他对象,如下所示:

_Operation.Executed += OperationExecuted;

2) 在这个事件中,我们调用 StopProgress() 如下:

private void OperationExecuted (object sender, OperationEventArgs e)
{
    StopProgress(); 
}

3) StopProgress() 如下所示:

public void StopProgress()
{
    if (InvokeRequired)
    {
        Invoke(new MethodInvoker(StopProgress));
        return;
    }

    lblTemp.Text = "operation complete. ";// 1
    //progressBar1.Visible = false; // 2

    // with added locks the app totally hangs
    //lock (progressBar1)
    //{
    //    progressBar1.Visible = false;
    //}
}

当注释标记为“1”的行(在 StopProgress() 内)和取消注释“2”(这是所需的行为)时,我们偶尔会遇到竞速条件(运行应用程序 5-10 次后,我们会遇到竞速条件)。使用“1”行,它永远不会发生。也没有抛出异常(捕获/未捕获)。我们假设问题与“ProgressBar”本身有关。如果没有,这里可能是什么情况。任何关于如何追踪比赛条件(易受攻击的代码部分)的建议也非常感谢。谢谢。

4

2 回答 2

2

很难解释这一点,但这段代码中有一些强大的反模式会导致死锁。

首先,您使用 Control.Invoke() 来确保进度条更新发生在 UI 线程上。因此,完全没有必要使用lock语句,因为您已经知道更新只会发生在一个线程上。所以解除锁。

使用 Control.Invoke() 是一种不好的反模式。它特别容易导致死锁,因为它在 UI 线程执行委托目标之前无法完成。当在等待线程完成的 UI 线程上运行其他代码时,就会发生死锁。这永远不会发生,线程卡在 Invoke() 调用中,直到 UI 线程空闲时才能完成。UI 线程不能空闲,它一直在等待线程完成。死锁城。仅在绝对必要时才使用 Invoke() 并且担心它会导致死锁。始终支持 BeginInvoke() ,因为它不等待,因此不会导致死锁。而且这里不需要 Invoke(),也不需要知道它的返回值。

使用 Control.InvokeRequired 也是一种反模式。您几乎总是知道从工作线程调用方法。因此,测试 InvokeRequired 毫无意义,您希望它是true当它是false时,会发生非常糟糕的事情。这是很有可能的,当用户关闭表单并允许工作线程继续执行时会发生这种情况。这通常会导致您的代码因 ObjectDisposedException 而崩溃。但是您的代码没有到达那里,它很可能在此之前发生死锁,无论是在 Invoke 调用还是lock。您应该使用 InvokeRequired,但当它为 false 时抛出 InvalidOperationException。现在您添加了一个诊断程序,让您有机会找出问题所在。

通过使用已经处理这些事情的 .NET 类来取得成功。BackgroundWorker 可以手动完成您尝试执行的所有操作。现在,您还将有机会处理用户在工作线程仍在执行的情况下关闭表单的问题。这个答案的主题。

于 2013-06-24T10:15:29.310 回答
0

为了避免这种情况,很可能,您可以将代码

progressBar1.Visible = false

就在StopProgress(..)方法里面。

imo,这里的重点是,在 的情况下InvokeRequiredStopProgress(..)将被调用并且当代(它是多线程)您可以运行代码progressBar1.Visible = false;。这可能会产生不希望的结果。

因此,为了避免这种“分布” progressBar1.Visible = false;,如果可能的话,请进入方法本身。

希望这可以帮助。

于 2013-06-24T09:23:15.080 回答