【问题标题】:Deadlock in WPF from main thread and worker thread来自主线程和工作线程的 WPF 死锁
【发布时间】:2017-03-10 16:01:47
【问题描述】:

我有这样的代码:

private static var Locker = new Object();

private void SensitiveCode()
{
    lock (Locker)
    {
        SomeCodeSensitive(); .....
        Dispatcher.BeginInvoke(new Action(() =>
        {
            SomeCodeThatUpdatesUI();
        }));
        MoreCodeSensitive();
    }
}

我也有一个线程正在运行,做这样的事情:

while (X)
{
    SensitiveCode();
    Thread.Sleep(5000);
}

现在在我的主线程中,我有时会调用 SensitiveCode(); SensitiveCode() 处于锁定状态,因为不能同时从 2 个线程调用它。

嗯,问题是我不知道为什么,有时我发现死锁,有时在 UI 上(UI 冻结),有时在线程上。我不知道发生了什么,其中一个线程在 lock() 中停止,因为假设变量“Locker”正在用于另一个线程,但事实并非如此。我认为问题可能是锁内的 BeginInvoke,但我认为这不足以锁定此代码。

另外,这个问题很难重现,我测试了3个小时,只出现了2次。

有人知道它是什么吗?

谢谢!

更新由于问题:

SensitiveCode() 正在处理一些 AsyncObservableCollection 对象(来自http://www.thomaslevesque.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/ 的类)。

做一些事情,比如从数据库中读取并填充那些 AsyncObservableCollection()。我锁定此操作是因为我不能允许线程在用户同时更新我的​​ AsyncObservableCollection 时更新它。

另外,用户界面也会这样做

Dispatcher.BeginInvoke(new Action(() =>
        {
            if (tables.Count != TablesInWindow.Count)               
                TablesInWindow.ClearAndAddRange(tables);                
            else
            {
                if (tables.Where((t, i) => !TablesInWindow[i].Equals(t)).Any())                                            
                    TablesInWindow.ClearAndAddRange(tables);

            }

            ButtonPageBack.IsEnabled = canGoBack;
            ButtonPageNext.IsEnabled = canGoFoward;
        }));

更新 2:

再做一次,这里是死锁发生时的线程截图

http://i.imgur.com/8xyIy6h.png

MovePageForward 是我在 UI 上执行的操作,它包含以下代码:

public static void MakePageForward()
    {            
        lock (ConfigParameters.Lock)
        {
            _currentPage++;
            ShowTablesInWindow();
        }
    }

TimerConnectionTick 代码如下:

private static void TimerConnectionTick()
    {
        while (!_thisDisposing)
        {
            ShowTablesInWindow();

            if (!_thisDisposing)
                Thread.Sleep(5000);
        }
    }

【问题讨论】:

  • 你是在使用你没有使用 Dispatcher.Invoke 还是在调用堆栈下方的某个地方锁定(在 SomeCodeSensitive 或 MoreCodeSensitive 部分)?
  • 您能否为您的示例添加更多上下文,以便重现该问题。 lockthread.Sleep 都会阻塞任何试图进入的线程,再次需要更多上下文。一些奇怪的事情:Dispatcher 不是敏感代码 为什么会被锁定?而Locker不是敏感物体,为什么要用它来做锁,在锁块内有多少敏感物体被触动了?
  • None Dispatcher.Invoke SensitiveCode 中的代码。
  • 嗯...这不是敏感代码,我可以将其从锁中取出,但我不知道为什么它会有所帮助。无论如何,我的问题是我无法重现此问题。我在5小时的测试中发生了3次,真是令人头疼。我会尝试拿出 BeginInvoke 代码并将其发送给客户,以便他们在真实环境中对其进行测试,因为我不知道还能做什么......储物柜是我用来锁定的对象,它不敏感只是一个新的对象()。我在敏感代码中触及的是对数据库的调用和更新 AsyncObservableCollection 对象。

标签: c# wpf multithreading locking


【解决方案1】:

如果不查看所有代码,就很难调试。是否有可能在您的 SomeCodeThatUpdatesUI() 方法中的某个地方也触发了对 Locker 的锁定?如果您以递归方式触发锁,它将毫无问题地通过,但是如果您在 Dispatcher 线程上的 BeginInvoke 中这样做(这可能与您提到的从不同线程调用的当前线程不同),它将锁定并导致死锁。

如果是这种情况,也许您可​​以在需要锁定时进行重构,或者使用 ReaderWriterLockSlim (https://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim(v=vs.110).aspx) 之类的东西

【讨论】:

  • 更新了更多信息的问题
  • 我看到了您的更新,但是我只能猜测您的其余代码在做什么,以及每个锁有多少个入口点。您确定 Locker 锁上发生了死锁吗?如果您可以导致它再次发生,请从进程中捕获转储文件并查看活动线程的调用堆栈。
  • 是的,我做到了,看看我链接的图片:
猜你喜欢
  • 2013-01-10
  • 1970-01-01
  • 1970-01-01
  • 2010-09-10
  • 2018-06-16
  • 2016-04-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多