dan*_*die 6 .net c# multithreading
出于某种原因,这种安全的方法引发了一个典型的例外.
跨线程操作无效:控制'statusLabel'从其创建的线程以外的线程访问.
显然,当需要调用时,此代码应通过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();
}
Run Code Online (Sandbox Code Playgroud)
[UPDATE2]
在实施John Saunders的建议后,仍然遇到了同样的错误
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, Int32 y, Int32 width, Int32 height, 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, Boolean measureOnly, 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 affectedProperty)
at System.Windows.Forms.Layout.LayoutTransaction.DoLayout(IArrangedElement elementToLayout, IArrangedElement elementCausingLayout, 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__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
Run Code Online (Sandbox Code Playgroud)
[更新]这是问题完整性的堆栈跟踪.
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, Int32 y, Int32 width, Int32 height, 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, Boolean measureOnly, 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 affectedProperty)
at System.Windows.Forms.Layout.LayoutTransaction.DoLayout(IArrangedElement elementToLayout, IArrangedElement elementCausingLayout, 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
Run Code Online (Sandbox Code Playgroud)
Nic*_*cki 19
这可能与您的情况直接相关,也可能不直接相关,但可以提供线索.记住Windows窗体的一个重要漏洞抽象Handle
是在实际需要之前不会创建窗口.该Handle
属性仅hwnd
在第一次get
调用时创建真正的Windows ,当Control
实例化一个非衍生对象(如Windows窗体)时不会发生这种情况.(Control
毕竟,-derived对象只是一个.NET类.)换句话说,它是一个懒惰地初始化的属性.
我以前被这个烧过:我的问题是我在UI线程上正确实例化了一个表单,但是Show()
直到数据从一个已经运行的Web服务调用返回时我才知道它.工人线程.情节是,在工作线程完成其工作之前,作为检查的一部分访问该表单之前,没有人曾要求该表单.所以我的后台工作者线程问了表格:我需要吗?然后表单的实现说:好吧,让我看看我的所以我可以看到我的内部创建了什么线程,然后我会看到你是否在同一个线程上.然后实施说:我还不存在,所以让我现在为自己创造一个.(你会看到它的发展方向.请记住,我们仍然在后台线程,无辜地访问该物业.)Handle
InvokeRequired
InvokeRequired
InvokeRequired
Handle
hwnd
Handle
hwnd
InvokeRequired
这导致Handle
(和它的底层hwnd
)在工作线程上创建,我没有拥有它,并且没有设置消息泵来处理Windows消息.结果:当我对以前隐藏的窗口进行其他调用时,我的应用程序被锁定,因为这些调用是在主UI线程上进行的,这合理地假设所有其他Control
来源的对象也已在此线程上创建.在其他情况下,这可能会导致奇怪的跨线程异常,因为它InvokeRequired
会意外地返回false,因为它Handle
是在与实例化表单的线程不同的线程上创建的.
但有时候只是.我有一些功能,用户可以Show()
通过菜单使表单自身,然后它会在自己填充背景中的数据时自动禁用(显示一个悸动动画).如果他们首先这样做,那么一切都会好起来的:它Handle
是在UI线程上创建的(在菜单项的事件处理程序中),并且InvokeRequired
当工作线程完成从Web服务检索数据时,表现得如预期的那样.但是如果我定期运行的后台线程(它是一个事件调度程序,类似于Outlook中的Event Reminder对话框)访问Web服务并试图弹出表单,而用户还没有Show()
它,那么工作线程的触摸InvokeRequired
会引起上述的胃灼热诱发行为.
祝你的heisenbug好运!
尝试从Label覆盖,以创建新的标签类.覆盖text属性并在其上放置断点.更改可疑标签以改为使用新的调试类.我还发现,如果您需要确定更新的位置和方式,这种技术非常适合对表单进行一些基本的分析和/或调试.
public class MyLabel : Label
{
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
使用你的代码并尝试捕获heisenbug,你将能够在每次访问标签时中断,所以如果它来自你不期望和/或不是你的调用代码路径的堆栈跟踪,你有你的错误?
从调试器启动应用程序或独立运行时是否看到此错误?
在调试器中运行时,.NET Framework 错误地引发了此异常。调试器有一些特殊之处,会导致控件的 InvokeRequired 标志错误地返回 true,即使代码在主 UI 线程内运行也是如此。这对我来说非常一致,而且总是在我们的控制权被处置之后发生。我们的堆栈跟踪如下所示:
System.InvalidOperationException:跨线程操作无效:从创建它的线程以外的线程访问控制“cboMyDropDown”。 在 System.Windows.Forms.Control.get_Handle() 在 System.Windows.Forms.TextBox.ResetAutoComplete(布尔力) 在 System.Windows.Forms.TextBox.Dispose(布尔处置) 在 OurApp.Controls.OurDropDownControl.Dispose(布尔处置) 在 System.ComponentModel.Component.Dispose()
您可以从.NET Framework源代码中看到错误的来源:
public class Control //...
{ //...
public IntPtr Handle
{
get
{
if ((checkForIllegalCrossThreadCalls && !inCrossThreadSafeCall) && this.InvokeRequired)
{
throw new InvalidOperationException(System.Windows.Forms.SR.GetString("IllegalCrossThreadCall", new object[] { this.Name }));
}
if (!this.IsHandleCreated)
{
this.CreateHandle();
}
return this.HandleInternal;
}
}
}
Run Code Online (Sandbox Code Playgroud)
在调试器中运行时,checkForIllegalCrossThreadCalls
为 true,inCrossThreadSafeCall
为 false,this.InvokeRequired
尽管在 UI 线程中但仍为 true!
请注意,Control.InvokeRequired
最终会这样做:
int windowThreadProcessId = System.Windows.Forms.SafeNativeMethods.GetWindowThreadProcessId(ref2, out num);
int currentThreadId = System.Windows.Forms.SafeNativeMethods.GetCurrentThreadId();
return (windowThreadProcessId != currentThreadId);
Run Code Online (Sandbox Code Playgroud)
另请注意,我们的应用程序使用 .NET Framework 2.0。不确定这是否是未来版本中的问题,但我想无论如何我都会为后代写下这个答案。