在长时间运行期间泵送 Windows 消息?

Posted

技术标签:

【中文标题】在长时间运行期间泵送 Windows 消息?【英文标题】:Pumping Windows Messages During Long Operation? 【发布时间】:2011-06-15 22:59:52 【问题描述】:

我正在运行的大型操作中收到以下消息:

CLR 无法转换 从 COM 上下文 0x1fe458 到 COM 上下文 0x1fe5c8 持续 60 秒。这 拥有目的地的线程 上下文/公寓最有可能 要么进行非抽水等待,要么 处理很长时间的运行 无抽水操作 Windows 消息。这种情况一般有 负面的性能影响,并可能 甚至导致应用程序变成 无响应或内存使用 随着时间的推移不断积累。到 避免这个问题,全单 线程单元 (STA) 线程 应该使用抽水等待原语 (例如 CoWaitForMultipleHandles)和 在很长一段时间内定期发送消息 运行操作。

如何发送 Windows 消息,以便在长时间操作时不再出现此错误?

【问题讨论】:

有完整源代码的最终解决方案吗? 【参考方案1】:

目前还不清楚上下文到底是什么——您是在 WinForms 或 WPF 应用程序的 UI 线程上执行一些长时间运行的任务吗?如果是这样,请不要这样做 - 使用BackgroundWorker,或者直接在线程池或新线程上运行任务(如果需要更新 UI,可能使用Control.Invoke/BeginInvokeDispatcher)。如果您的大操作使用正在抱怨的 COM 组件,那就更难了...

【讨论】:

是的,这是在 WinForm 中(抱歉没有在 OP 中指定)。我将尝试在后台工作人员中实现此代码,谢谢! 完成了这个,工作就像一个魅力,再次感谢! @Jon Skeet 您能否详细说明 COM 组件部分?我们有多个线程访问 COM 对象,这会导致 OP 问题。 @odyodyodys:听起来我们需要有关您正在使用的 COM 组件的更多信息 - 特别是它使用的线程模型。 我有一个线程处理大量数据,而 UI 等待进度消息。听起来我做得对,但调试器会抛出此消息。等待消息时我应该在 UI 线程中做什么?【参考方案2】:

据我所知,只有附加的调试器才会发生这种情况。您永远不会在生产中遇到此异常。

【讨论】:

确实没有,因为消息是由 VS 调试器的管理单元生成的。尽管如此,这一事实并没有消除消息警告的潜在问题。 谢谢,我也注意到我的调试器显示了这个警告,即使我的 UI 在那一刻完全响应(我什至有一个我使用 Task.Run 运行的后台任务的进度条,并且进度条是活动的并且是动画的,但是 Visual Studio 仍然显示了这个异常。也许在我的情况下,这与我在 Windows 窗体上使用 WebBrowser 元素的事实有关......【参考方案3】:

如果在调试器中发生这种情况,可能是由于 ContextSwitchDeadlock MDA,您可以将其关闭(使用 Visual Studio 中的“异常”窗口)。然而,这表明一个更大的问题——你不应该在你的 UI 线程上执行长时间运行的操作。

【讨论】:

【参考方案4】:

在这些情况下,我倾向于使用Application.DoEvents。我不知道这是否适用于您的情况。它需要对 System.Windows.Forms 的引用,但也可以在控制台应用程序中使用。

或者,您可以尝试多线程您的应用程序。

【讨论】:

Application.DoEvents 在这里几乎总是 错误 解决方案,IMO。可能存在一些需要它的边缘情况,但如果它只是“我正在执行一个真正不应该在 UI 线程中运行的长时间运行的操作”,那么将其移出 UI 线程是正确的解决方案。 我完全同意。我最近没有看到错误消息,但我隐约记得我不仅在 UI 线程上看到了它。我认为我已经在长时间运行的 COM 请求中看到了它,但我可能是错的。【参考方案5】:

传统的win32方法是:

void PumpMessages()

    MSG msg;
    for( ;; ) 
        if( !PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) 
            return;
        
        if( msg.message == WM_QUIT ) 
            s_stopped = true;
            return;
        
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    

但我猜你正在使用 .NET。

【讨论】:

【参考方案6】:

我在从 WinForms 项目切换到 ConsoleApp 项目时遇到了这个问题。 Program.cs 中的 Main() 方法从 WinForms 模板中遗留了一个不必要的 [STAThread] 属性。删除该属性会使错误消失。

【讨论】:

【参考方案7】:

您应该在单独的线程上处理长时间运行的操作,以避免冻结 UI。这样也能解决上面的问题

【讨论】:

【参考方案8】:

我知道这是几年前提出的问题,但希望这对以后的其他人有所帮助。如果您不想担心做后台工作者或发送消息,一个简单的解决方法就是简单地更新 UI 上的内容。例如,我有一个只有我使用的工具,所以我不在乎它是否使用 UI 线程。因此,我只需将 UI 上的 textbox.text 更新为我正在处理的任何内容。这是代码的sn-p。这是一种非常hacky,并且可能不正确的专业方法,但它确实有效。

for (int i = 0; i < txtbxURL.LineCount; i++)

    mytest.NavigateTo(mytest.strURL);
    mytest.SetupWebDoc(mytest.strURL);
    strOutput = mytest.PullOutPutText(mytest.strURL);
    strOutput = mytest.Tests(strOutput);
    mytest.CheckAlt(mytest.strURL);
    mytest.WriteError(txtbxWriteFile.Text);
    txtblCurrentURL.Text = mytest.strURL;
    //This ^^^ is what is being updated, which keeps the thread from throwing the exception noted above.

【讨论】:

以上是关于在长时间运行期间泵送 Windows 消息?的主要内容,如果未能解决你的问题,请参考以下文章

WinForm 应用程序 UI 在长时间运行期间挂起

使用 ReSharper,如何在长时间运行的单元测试期间显示调试输出?

C# 如何防止在长时间运行的查询期间因崩溃而丢失数据?

在应用程序关闭期间正确关闭一个可能运行很长时间循环的线程[重复]

在IIS托管的WCF服务中使用RabbitMQ Queue

如何在长时间运行的服务器操作期间与用户交互(例如确认对话框)?