长时间操作期间如何抽取Windows消息?

27
我在运行一个大操作时收到以下消息:

CLR无法在60秒内从COM上下文0x1fe458切换到COM上下文0x1fe5c8。拥有目标上下文/公寓的线程很可能正在进行非泵等待或处理非常长时间的操作而没有泵出Windows消息。这种情况通常会对性能产生负面影响,甚至可能导致应用程序变得无响应或内存使用持续累积。为避免此问题,所有单线程公寓(STA)线程都应该使用泵等待原语(例如CoWaitForMultipleHandles),并在长时间运行的操作期间定期泵出消息。

如何发送 windows 消息以使此错误不再发生?

有没有完整源代码的最终解决方案? - Kiquenet
8个回答

21

不清楚具体情境,您是在WinForms或WPF应用程序的UI线程上执行一些长时间运行的任务吗?如果是这样,请勿这样做-使用BackgroundWorker,或直接在线程池或新线程上运行任务(可能使用Control.Invoke/BeginInvokeDispatcher,如果需要更新UI)。如果您的大型操作使用了投诉的COM组件,那么将会更加困难...


是的,这个在一个WinForm中(抱歉在原始帖子中没有说明)。我会尝试在后台工作者中实现这段代码,谢谢! - sooprise
实现完成了,非常顺利,再次感谢! - sooprise
1
@Jon Skeet,您能否详细说明一下COM组件部分?我们有多个线程访问COM对象,这导致了操作系统的问题。 - Odys
@odyodyodys:听起来我们需要更多关于你正在使用的COM组件的信息,特别是它使用的线程模型。 - Jon Skeet
我有一个线程正在处理大量数据,而UI等待进度消息。听起来我做得很对,但调试器却抛出了这些消息。在等待消息时,我应该在UI线程中做什么? - James
@James:听起来你应该创建一个新的问题来展示你正在做什么。 - Jon Skeet

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

3
确实不行,因为该消息是由VS调试器的插件生成的。但是,这个事实并不能消除消息所警示的潜在问题。 - ivan_pozdeev
谢谢,我也注意到了即使在我的UI完全响应时我的调试器仍然显示此警告(我甚至为我的后台任务使用Task.Run运行一个进度条,进度条仍在运转和动画,但Visual Studio仍显示这个异常。也许在我的情况下,这与我在Windows表单上使用WebBrowser元素有关... - JustAMartin

4
如果在调试器中出现这种情况,可能是由于ContextSwitchDeadlock MDA引起的,您可以关闭它(使用Visual Studio中的异常窗口)。然而,这表明存在更大的问题--您不应该在UI线程上执行长时间运行的操作。

3
我倾向于在这些情况下使用Application.DoEvents。但我不知道这是否适用于你的情况。它需要引用System.Windows.Forms,但也可以在控制台应用程序中使用。
或者,您可以尝试将应用程序多线程化。

1
在我看来,Application.DoEvents几乎总是错误的解决方案。可能有一些边缘情况需要它,但如果只是“我正在执行一个长时间运行的操作,这个操作不应该在UI线程中”,那么将其移出UI线程是正确的解决方案。 - Jon Skeet
2
我完全同意。我最近没有看到过这个错误消息,但我依稀记得我不仅在UI线程上看到过它。我认为我在长时间运行的COM请求上看到过它,但我可能错了。 - Matthew Steeples

2
传统的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技术。


1

当我从WinForms项目切换到ConsoleApp项目时,遇到了这个问题。在Program.cs中的Main()方法中有一个不必要的[STAThread]属性,这是从WinForms模板中剩下的。删除该属性可以消除错误。


0

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


-1

我知道这个问题已经被问了好几年,但希望这能帮助到其他人。如果你不想担心后台工作程序或消息传递,一个简单的解决方法就是在UI上更新某些内容。例如,我有一个只有我使用的工具,所以我不在意它是否使用UI线程。因此,我只需在UI上更新一个文本框的文本,以反映我正在处理的内容。以下是代码片段。这是一种非常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.
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接