c# – 后台任务有时能够更新UI吗?
我刚回答了一个关于任务是否可以更新UI的问题.当我玩我的代码时,我意识到我不清楚自己的一些事情.
如果我有一个带有一个控件txtHello的Windows窗体,我可以从Task更新UI,看来,如果我立即在Task.Run上执行它: public partial class Form1 : Form { public Form1() { InitializeComponent(); Task.Run(() => { txtHello.Text = "Hello"; }); } } 但是,如果我Thread.Sleep甚至5毫秒,则抛出预期的CrossThread错误: public partial class Form1 : Form { public Form1() { InitializeComponent(); Task.Run(() => { Thread.Sleep(5); txtHello.Text = "Hello"; //kaboom }); } } 我不确定为什么会这样.对于极短的运行任务,是否存在某种优化? 解决方法
你没有发布异常堆栈跟踪,但我希望它看起来像这样:
System.InvalidOperationException: Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on. at System.Windows.Forms.Control.get_Handle() at System.Windows.Forms.Control.set_WindowText(String value) at System.Windows.Forms.TextBoxBase.set_WindowText(String value) at System.Windows.Forms.Control.set_Text(String value) at System.Windows.Forms.TextBoxBase.set_Text(String value) at System.Windows.Forms.TextBox.set_Text(String value) at WindowsFormsApplicationcSharp2015.Form1.<.ctor>b__0_0() in D:testWindowsFormsApplicationcSharp2015Form1.cs:line 27 我们可以看到从Control.Handle getter属性抛出异常.事实上,如果我们看一下该物业的source code,那就像预期的那样: public IntPtr Handle { get { if (checkForIllegalCrossThreadCalls && !inCrossThreadSafeCall && InvokeRequired) { throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall,Name)); } if (!IsHandleCreated) { CreateHandle(); } return HandleInternal; } } 有趣的是,当我们查看调用Control.Handle的代码时.在这种情况下,这是Control.WindowText setter属性: set { if (value == null) value = ""; if (!WindowText.Equals(value)) { if (IsHandleCreated) { UnsafeNativeMethods.SetWindowText(new HandleRef(window,Handle),value); } else { if (value.Length == 0) { text = null; } else { text = value; } } } } 请注意,仅当IsHandleCreated为true时才会调用Handle属性. 为了完整起见,如果我们查看IsHandleCreated的代码,我们会看到以下内容: public bool IsHandleCreated { get { return window.Handle != IntPtr.Zero; } } 因此,您没有得到异常的原因是因为在Task执行时,尚未创建窗口句柄,这是预期的,因为Task在窗体的构造函数中启动,即在表格甚至显示. 在创建窗口句柄之前,修改属性不需要UI线程中的任何工作.因此,在程序开始的这个小时间窗口中,似乎可以从非UI线程调用控件实例上的方法而不会出现“交叉线程”异常.但显然,这个特殊的小时间窗口的存在并没有改变这样一个事实,即我们应该始终确保从UI线程调用控制方法是安全的. 要证明窗口句柄创建的时间是获取(或不)“交叉线程”异常的决定因素,请尝试修改示例以在启动任务之前强制创建窗口句柄,并注意如何即使没有睡眠,您现在也会始终获得预期的异常: public partial class Form1 : Form { public Form1() { InitializeComponent(); // Force creation of window handle var dummy = txtHello.Handle; Task.Run(() => { txtHello.Text = "Hello"; // kaboom }); } } 相关文件:Control.Handle
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |