带锁的C#多线程导致死锁

时间:2014-01-15 19:46:12

标签: c# multithreading locking deadlock

我在任务中运行此代码:

foreach (a in as) // yield return
{
   component.Notify(...); // This component locks internally.
   component.PropertyChanged += (o, args) =>
   {
       // Cancel out of task by adding another screen.
       MainWindow.AddContent(new OtherComponent()); // WPF
   }
}

现在,当在错误的时刻取消任务时,.Notify函数在已经取消后仍然会被调用,因为该任务仍在运行。我想要的是当调用.PropertyChanged事件时,不应再有.Notify调用之后。我当然可以在循环中使用一个相当简单的布尔值来解决这个问题,它会在下一次迭代中取消它,例如:

foreach (a in as) // yield return
{
   if (requestCancel)
       return;
   component.Notify(...); // This component locks internally.
   component.PropertyChanged += (o, args) =>
   {
       requestCancel = true;
   }
}

此处的一个问题是foreach (a in as)语句可能需要一段时间才能返回下一次迭代,因此在实际取消之前可能需要很长时间。我尝试使用锁定requestCancel操作,然后检查requestCancel是否为true,但这会导致死锁,因为component在所有操作中都在内部使用锁。我不能改变它,因为它是第三方组件。我也试过一个信号量,一个读写器,但我总是陷入僵局。比如这个:

foreach (a in as) // yield return
{
   _semaphore.WaitOne();
   if (requestCancel)
       return;

   component.Notify(...); // This component locks internally.
   component.PropertyChanged += (o, args) =>
   {
       _semaphore.WaitOne();
       requestCancel = true;
   }
   _semaphore.Release();
}

基本上,.Notify被阻止,因为PropertyChanged仍在执行(由于内部都使用了锁)。这反过来导致PropertyChanged正在等待信号量或readerwriterlock的情况,而另一个块永远不会释放信号量,因为它正在等待PropertyChanged因锁定而完成。

我确实需要尽快取消此代码,并且在用户取消后无法再调用.Notify。在摆弄了一段时间之后,我已经没想完了。如果有任何不清楚的地方,请不要犹豫,要求提供进一步的信息。

1 个答案:

答案 0 :(得分:0)

我认为,所有代码都在这样的一个线程上执行:

  1. 输入通知()
  2. 调用PropertyChanged处理程序
  3. 离开通知()
  4. 在这种情况下,信号量将始终阻止您的代码,因为在步骤2中您等待信号量发出信号,但这只发生在第3步之后。试试这个:

    foreach (a in as) // yield return
    {
        component.PropertyChanged += (o, args) => requestCancel = true;
    
        component.Notify(...);
    
        // break out of the loop here to prevent getting next item from the enumerator
        if (requestCancel)
            break;
    }
    
    // do whatever you want to do after cancel
    if (requestCancel)
        MainWindow.AddContent(new OtherComponent());