c# – Winforms多线程UI操作的随机跨线程操作异常

前端之家收集整理的这篇文章主要介绍了c# – Winforms多线程UI操作的随机跨线程操作异常前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
出于某种原因,这种安全的方法引发了一个典型的例外.

Cross-thread operation not valid:
Control ‘statusLabel’ accessed from a
thread other than the thread it was
created on.

显然,当需要调用时,此代码应通过Invoke调用匿名方法.
但每隔一段时间就会发生异常.

有人有过类似的问题吗?

private void SetProgressBarValue(int progressPercentage)
    {
        Action setValue = () => 
        {
            var value = progressPercentage;
            if (progressPercentage < 0)
                value = 0;
            else if (progressPercentage > 100)
                value = 100;
            statusProgressBar.Value = value;
            statusLabel.Text = string.Format("{0}%",value);
        };
        if (Invokerequired)
            Invoke(setValue);
        else
            setValue();
    }

[UPDATE2]
执行John Saunders’s suggestion后,仍然遇到同样的错误

at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.SetBoundscore(Int32 x,Int32 y,Int32 width,Int32 height,BoundsSpecified specified)
at System.Windows.Forms.ToolStrip.SetBoundscore(Int32 x,BoundsSpecified specified)
at System.Windows.Forms.ToolStrip.System.Windows.Forms.Layout.IArrangedElement.SetBounds(Rectangle bounds,BoundsSpecified specified)
at System.Windows.Forms.Layout.DefaultLayout.xLayoutDockedControl(IArrangedElement element,Rectangle newElementBounds,Boolean measureOnly,ref Size preferredSize,ref Rectangle remainingBounds)
at System.Windows.Forms.Layout.DefaultLayout.LayoutDockedControls(IArrangedElement container,Boolean measureOnly)
at System.Windows.Forms.Layout.DefaultLayout.xLayout(IArrangedElement container,ref Size preferredSize)
at System.Windows.Forms.Layout.DefaultLayout.LayoutCore(IArrangedElement container,LayoutEventArgs args)
at System.Windows.Forms.Layout.LayoutEngine.Layout(Object container,LayoutEventArgs layoutEventArgs)
at System.Windows.Forms.Control.OnLayout(LayoutEventArgs levent)
at System.Windows.Forms.ScrollableControl.OnLayout(LayoutEventArgs levent)
at System.Windows.Forms.Form.OnLayout(LayoutEventArgs levent)
at System.Windows.Forms.Control.PerformLayout(LayoutEventArgs args)
at System.Windows.Forms.Control.System.Windows.Forms.Layout.IArrangedElement.PerformLayout(IArrangedElement affectedElement,String affectedProperty)
at System.Windows.Forms.Layout.LayoutTransaction.DoLayout(IArrangedElement elementToLayout,IArrangedElement elementCausingLayout,String property)
at System.Windows.Forms.Control.PerformLayout(LayoutEventArgs args)
at System.Windows.Forms.Control.System.Windows.Forms.Layout.IArrangedElement.PerformLayout(IArrangedElement affectedElement,String property)
at System.Windows.Forms.ToolStripItem.InvalidateItemLayout(String affectedProperty,Boolean invalidatePainting)
at System.Windows.Forms.ToolStripItem.OnTextChanged(EventArgs e)
at System.Windows.Forms.ToolStripItem.set_Text(String value)
at App.Image.Replace.ReplacementImageProcessForm.<>c__DisplayClassa.<>c__DisplayClassc.<SetProgressBarValue>b__9() in ReplacementImageProcessForm.cs: line 147
at App.Image.Replace.ReplacementImageProcessForm.InvokeIfNecessary(Control control,Action setValue) in ReplacementImageProcessForm.cs: line 156
at App.Image.Replace.ReplacementImageProcessForm.<>c__DisplayClassa.<SetProgressBarValue>b__7() in ReplacementImageProcessForm.cs: line 145
at App.Image.Replace.ReplacementImageProcessForm.InvokeIfNecessary(Control control,Action setValue) in ReplacementImageProcessForm.cs: line 156
at App.Image.Replace.ReplacementImageProcessForm.SetProgressBarValue(Int32 progressPercentage) in ReplacementImageProcessForm.cs: line 132
at App.Image.Replace.ReplacementImageProcessForm.replacer_BeginReplace(Object sender,EventArgs e) in ReplacementImageProcessForm.cs: line 74
at App.Image.Replace.DocumentReplacer.OnBeginReplace() in IDocumentReplacer.cs: line 87
at App.Image.Replace.DocumentReplacer.Replace(Int32 documentId,String replacementDocumentPath) in IDocumentReplacer.cs: line 123

[更新]这是问题完整性的堆栈跟踪.

at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.SetBoundscore(Int32 x,Boolean invalidatePainting)
at System.Windows.Forms.ToolStripItem.OnTextChanged(EventArgs e)
at System.Windows.Forms.ToolStripItem.set_Text(String value)
at App.Image.Replace.ReplacementImageProcessForm.<>c__DisplayClass8.<SetProgressBarValue>b__7() in ReplacementImageProcessForm.cs: line 114
at App.Image.Replace.ReplacementImageProcessForm.SetProgressBarValue(Int32 progressPercentage) in ReplacementImageProcessForm.cs: line 119
at App.Image.Replace.ReplacementImageProcessForm.replacer_BeginReplace(Object sender,EventArgs e) in ReplacementImageProcessForm.cs: line 76
at App.Image.Replace.DocumentReplacer.OnBeginReplace() in IDocumentReplacer.cs: line 72
at App.Image.Replace.DocumentReplacer.Replace(Int32 documentId,String replacementDocumentPath) in IDocumentReplacer.cs: line 108

解决方法

这可能与您的情况直接相关,也可能不直接相关,但可以提供线索.要记住Windows窗体的一个重要漏洞抽象是在实际需要之前不会创建窗口句柄. Handle属性仅在第一次get调用时创建真正的Windows hwnd,当实例化Control派生对象(如Windows窗体)时不会发生这种情况. (毕竟,Control-derived对象只是一个.NET类.)换句话说,它是一个懒惰地初始化的属性.

我以前被这个烧过:我的问题是我在UI线程上正确实例化了一个表单,但是直到数据从已经进行的Web服务调用返回时我才显示它.在工作线程上运行.这种情况是,在工作线程完成其工作之后,作为Invokerequired检查的一部分访问它之前,没有人曾要求表单的Handle.所以我的后台工作者线程询问了表单:我需要Invokerequired吗?然后表单的Invokerequired实现说:好吧,让我看一下我的Handle,这样我就可以看到我的内部hwnd创建了什么线程,然后我会看看你是否在同一个线程上.然后Handle实现说:我还不存在,所以让我现在为自己创造一个hwnd. (你会看到它的发展方向.请记住,我们仍然在后台线程上,无辜地访问Invokerequired属性.)

这导致Handle(和它的底层hwnd)在工作线程上创建,我没有拥有它,并且没有设置消息泵来处理Windows消息.结果:当我对以前隐藏的窗口进行其他调用时,我的应用程序被锁定,因为这些调用是在主UI线程上进行的,这合理地假设所有其他的Control派生对象也已在此线程上创建.在其他情况下,这可能会导致奇怪的跨线程异常,因为Invokerequired会意外返回false,因为Handle是在与实例化表单的线程不同的线程上创建的.

但有时候只是.我有一些功能,用户可以通过菜单使表单显示()本身,然后它会在自己填充背景中的数据时自动禁用(显示一个跳动动画).如果他们首先这样做,那么一切都会好的:Handle是在UI线程上创建的(在菜单项的事件处理程序中),因此当工作线程完成从Web服务检索数据时,Invokerequired按预期运行.但是如果我定期运行的后台线程(它是一个事件调度程序,类似于Outlook中的Event Reminder对话框)访问Web服务并尝试弹出表单,并且用户还没有Show()n,那么工作者线程的触摸Invokerequired将导致上述的引起胃灼热的行为.

祝你的heisenbug好运!

原文链接:https://www.f2er.com/csharp/98144.html

猜你在找的C#相关文章