我可以从后台线程更新UI,为什么?
大家都知道,不允许从后台线程更新UI(或不是?)
我做了一个小实验。 这是一段代码:
var thread = new Thread(() => progressBar1.Increment(50)); thread.IsBackground = true; thread.Start(); thread.Join();
我将此代码放在一些按钮单击处理程序中。 知道吗? 我的进度条正在递增…从后台线程。 现在我很困惑。 我不明白这是怎么可能的,我做错了什么
我可以从后台线程更新UI,为什么?
最有可能的是,当你在调试器外面运行时,你可以这样做。 这是因为该保护由Control.CheckForIllegalCrossThreadCalls属性控制。 我们来看看参考源
private static bool checkForIllegalCrossThreadCalls = Debugger.IsAttached; public static bool CheckForIllegalCrossThreadCalls { get { return checkForIllegalCrossThreadCalls; } set { checkForIllegalCrossThreadCalls = value; } }
如您所见,只有在调试时才会默认启用此保护。
如果在Main
方法中添加以下行(在Application.Run
之前)
Control.CheckForIllegalCrossThreadCalls = true;
你的代码将不再有效。
大家都知道,不允许从后台线程更新UI
不是每个人都知道。 但这是事实。 从UI线程以外的线程更新大多数UI元素是错误的。
我不明白它是怎么可能的
你把一堆沙子倒进汽车的油底壳里。 你不应该这样做。 那么你怎么可能这样做呢? 好像问一个奇怪的问题,不是吗?
汽车是否需要阻止你这样做? 不是。当您这样做时,发动机是否需要停止运转? 不会。发动机最终会破裂吗? 大概。 但不一定。
设计你的汽车的工程师认为你不会做错事,所以他们没有建立任何安全系统来防止或发现这个问题。
我究竟做错了什么?
您正在从后台线程更新UI。 不要那样做。 但运行时不需要提供防止或检测错误的安全系统。 当你做一些你不应该做的事情时,不需要产生错误。 如果它想要或不想要,它可以这样做。
或者,这样想一想。 假设您正在实现运行时,以便它具有您认为具有的属性:运行时将始终检测到错误的跨线程调用并在发生时产生错误。 这是一个function。 有人必须设计,编写,测试和维护该代码。 代码是什么样的,与之相关的所有成本是多少?
就像varocarbas所说,你放入设计器的任何组件都会抛出Cross threadexception。
要在线程之间访问你需要使用invke,beginInvoke。
试着跑
BackgroundWorker bw = new BackgroundWorker(); bw.DoWork += (s1,e1) => { textBoxt1.Text = "foo"; // Exception here } bw.RunWorkerCompleted += (1s, e1) => { textBoxt1.Text = "foo"; // No exception here off UI thread } bw.RunWorkerAsync();
而是替换
bw.DoWork += (s1,e1) => { this.Invoke((MethodInvoker) delegate { textBoxt1.Text = message; }); // No Exception now }