我的表单包含一个文本框txtOutput
和以下来源:
public partial class frmMain : Form
{
private IProgress reporter;
public frmMain(string[] args)
{
InitializeComponent();
new anotherForm().ShowDialog();
reporter = new Progress<string>(txtOutput.AppendText);
Task.Run((Action)ReadReply);
}
private void ReadReply()
{
reporter.Report("test");
}
}
如果你运行它,会抛出异常:
System.InvalidOperationException: Cross-thread operation not valid: Control 'txtOutput' accessed from a thread other than the thread it was created on.
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.TextBoxBase.GetSelectionStartAndLength(Int32& start, Int32& length)
at System.Windows.Forms.TextBoxBase.AppendText(String text)
at System.Progress`1.InvokeHandlers(Object state)
at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
如果你重新排序这两行,一切正常:
reporter = new IProgress<string>(txtOutput.AppendText);
new anotherForm().ShowDialog();
Progress.Report()
会抛出此异常?创建它是为了避免这种例外吗?ShowDialog()
会中断IProgress.Report()
?答案 0 :(得分:1)
如果您使用Progress<T>
作为IProgress<T>
,预计其行为将如此。
Progress<T>
会在SyncronizationContext
构建时捕获Report
,使用SyncronizedContext
时会尝试使用捕获的SyncronizationContext.Current
进行调用:
来自docs:
提供给使用ProgressChanged事件注册的构造函数或事件处理程序的任何处理程序都是通过构造实例时捕获的SynchronizationContext实例调用的。
如果您在ShowDialog
之前和之后检查WindowsFormsSynchronizationContext
,则会发现它已从SynchronizationContext
更改为SyncronizationContext
。
原来的Progress<T>
丢失了。
这就是为什么如果你在ShowDialog
之前构建SyncronizationContext
。
可以通过源代码找到.ShowDialog
丢失的原因:
当表单显示为WindowsFormsSynchronizationContext
时,会导致为其生成一个新的消息循环,并且只有在以前不存在see here时才会为其安装新的WindowsFormsSynchronizationContext
然后在关闭时将其卸载到上一个(here)。
由于在显示第二个表单时上下文已经是{{1}},因此没有创建新的上下文,因此在关闭时导致上下文设置为构造主表单之前的上下文。