您好,欢迎访问一九零五行业门户网

从0自学C#02--子线程访问主线程(UI线程)控件

如果使用多线程处理来提高 windows 窗体应用程序的性能,则你必须确保以线程安全的方式调用控件。
访问 windows 窗体控件不是本身就线程安全的。如果有两个或两个以上线程操作控件的状态,则可能迫使该控件处于不一致状态。可能出现其他与线程相关的 bug,例如争用条件和死锁。请务必确保以线程安全的方式访问控件。
1.初学者常常遇到的问题
从未使用 invoke 方法创建控件的线程调用控件是不安全的。下面是一个非线程安全的调用示例。运行时会引发 invalidoperationexception 消息,报错“从并未创建该控件的线程访问该控件 控件名称”。
// this event handler creates a thread that calls a // windows forms control in an unsafe way.private void settextunsafebtn_click( object sender, eventargs e) { this.demothread = new thread(new threadstart(this.threadprocunsafe)); this.demothread.start(); }// this method is executed on the worker thread and makes// an unsafe call on the textbox control.private void threadprocunsafe() { this.textbox1.text = "this text was set unsafely."; }
2.解决方法
如需对 windows 窗体控件进行线程安全的调用。
①查询控件的 invokerequired 属性。
②若 invokerequired 返回 true,则用实际调用控件的委托来调用 invoke。
③若 invokerequired 返回 false,则请直接调用控件。
这里分同步执行委托和异步执行委托。
在以下代码示例中,在 threadprocsafe 方法中实现了线程安全的调用,该方法由后台线程执行。若 textbox 控件的 invokerequired 返回 true,则 threadprocsafe 方法创建一个 settextcallback 实例并将其传递到窗体的 invoke 方法。这导致在创建了 settext 控件的线程上调用 textbox 方法,并且在该线程上下文中直接设置 text 属性。
// this event handler creates a thread that calls a // windows forms control in a thread-safe way. private void settextsafebtn_click( object sender, eventargs e) { this.demothread = new thread(new threadstart(this.threadprocsafe)); this.demothread.start(); }// this method is executed on the worker thread and makes // a thread-safe call on the textbox control. private void threadprocsafe() { this.settext("this text was set safely."); }// this delegate enables asynchronous calls for setting // the text property on a textbox control.delegate void settextcallback(string text); // this method demonstrates a pattern for making thread-safe // calls on a windows forms control. // // if the calling thread is different from the thread that // created the textbox control, this method creates a // settextcallback and calls itself asynchronously using the// invoke method. // // if the calling thread is the same as the thread that created // the textbox control, the text property is set directly. private void settext(string text) { // invokerequired required compares the thread id of the // calling thread to the thread id of the creating thread. // if these threads are different, it returns true. //this.textbox1.invokerequired will be replaced by //this.invokerequired, if want to set many controls' //attribute or text. if (this.textbox1.invokerequired)// or this.invokerequired { settextcallback d = new settextcallback(settext); this.invoke(d, new object[] { text }); } else { this.textbox1.text = text; } }
3.backgroundworker组件
在应用程序中实现多线程的首选方式是使用 backgroundworker 组件。 backgroundworker 组件为多线程处理使用事件驱动模型。后台线程运行你的 dowork 事件处理程序,创建了你的控件的线程运行 progresschanged 和 runworkercompleted 事件处理程序。你可以从 progresschanged 和 runworkercompleted 事件处理器中调用控件。
①创建一种方法来进行你想在后台线程中进行的工作。不要调用由此方法中的主线程所创建的控件。
②创建一种方法来报告后台工作结束后的后台工作结果。 在此方法中可以调用主线程创建的控件。
③将步骤 1 中创建的方法绑定到 dowork 实例中的 backgroundworker 事件,并将步骤 2 中创建的方法绑定到同一实例的 runworkercompleted 事件。
④若要启动后台线程,请调用 runworkerasync 实例的 backgroundworker 方法。
在以下代码示例中,dowork 事件处理程序使用 sleep 来模拟需要花费一些时间的工作。它不会调用该窗体的 textbox 控件。textbox 控件的 text 属性直接在 runworkercompleted 事件处理程序中设置。
// this backgroundworker is used to demonstrate the // preferred way of performing asynchronous operations.private backgroundworker backgroundworker1; // this event handler starts the form's // backgroundworker by calling runworkerasync. // // the text property of the textbox control is set // when the backgroundworker raises the runworkercompleted // event.private void settextbackgroundworkerbtn_click( object sender, eventargs e) { this.backgroundworker1.runworkerasync(); }// this event handler sets the text property of the textbox // control. it is called on the thread that created the // textbox control, so the call is thread-safe. // // backgroundworker is the preferred way to perform asynchronous // operations.private void backgroundworker1_runworkercompleted( object sender, runworkercompletedeventargs e) { this.textbox1.text = "this text was set safely by backgroundworker."; }
也可通过使用 progresschanged 事件来报告后台任务的进度。如需包含该事件的示例,请参阅 backgroundworker。
以上就是 从0自学c#02--子线程访问主线程(ui线程)控件的内容。
其它类似信息

推荐信息