【问题标题】:Deadlock in code invoked by Dispatcher.Invoke from multiple threads由 Dispatcher.Invoke 从多个线程调用的代码中的死锁
【发布时间】:2019-08-26 11:26:55
【问题描述】:

我在混合模式 C#/C++ 应用程序中遇到了一个奇怪的问题,当多个线程在调度程序线程上运行相同的函数时,该应用程序最终会出现死锁。

代码的大体结构是这样的:

在 C++ 中:

std::thread t([this,req, dataProvider]() {
    AsyncRequestData(req, dataProvider);
});
SetThreadPriority(t.native_handle(), THREAD_PRIORITY_BELOW_NORMAL);

t.detach();

在 AsyncRequestData 内部,我交给了一个 C# 方法,该方法使用 Dispatcher 来运行一些 WPF 呈现代码:

在 C# 中

_dispatcher.Invoke(() =>
{
    var tileRender = new WpfTileRenderer(tileId);                    
    _drawObjectStyleProvider.RefreshTiledStyle = true;
    tileRender.Render(Viewport, filteredRenderList);
});

在 WpfTileRenderer 内部是一个新的跳转回本地代码,它执行线程锁定。 (我们使用 QReadWriteLock,但我怀疑我们会得到与 std::mutex 或任何其他同步对象相同的结果):

void UpdatePointSymbol(int idx, const BasicGeoTypes::GeoPos& pos)
{
    QWriteLocker lock(&_rwlock);
    ...
}

似乎发生的情况是,我们有两个请求都被移交给调度程序线程(如预期的那样),但是当第一个请求到达 QWriteLocker 时,执行跳转到新请求的开始。当第二个请求到达同一个 QWriteLocker 时,它会陷入死锁。

堆栈看起来像这样:

NativeBridge.UpdatePointSymbol
[External Code]
WpfTileRenderer.Render
[External Code]
NativeBridge.UpdatePointSymbol
[External Code]
WpfTileRenderer.Render
[External Code]

我知道这是一团糟,如果我们能避免在本机代码和托管代码之间来回切换,我会是一个更快乐的人,但恐怕这不会很快发生。

所以我要问的基本上是是否有已知的原因会发生这种情况。即调度程序是否可以部分运行一个方法,然后通过从调用它的不同线程启动一个新方法来继续?

根据文档 Dispatcher.Invoke 应该在当前调度程序线程上同步执行,所以我认为不是?

关于这种行为的其他原因有什么想法吗?

【问题讨论】:

  • 请分享您的Render 方法。
  • WPF 背负着一个糟糕的 Google 热门热门歌曲,始终青睐 BeginInvoke() 而不是 Invoke()。 C++ 背负着糟糕的微优化,总是喜欢支持递归的锁。
  • 在一个线程上同步,但不能跨多个线程。您只在更新期间锁定?插入和读取呢?我喜欢做的是有一个 Read_Write(Mode mode, ref data) ,其中 Mode 是读或写。然后将lock in method和use method用于所有的读写。这样你就不能同时读写了。

标签: c# c++ .net clr


【解决方案1】:

看来这个问题是由代码中其他地方的越界数组操作引起的。我怀疑这已经把堆栈搞得一团糟,以至于我得到了我上面描述的明显情况。

我以前见过类似的事情,应该在发帖前仔细检查一下。感谢那些回答的人,我真的很抱歉浪费了你的时间:-)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-01
    • 1970-01-01
    • 2014-03-20
    • 2018-06-16
    • 1970-01-01
    相关资源
    最近更新 更多