C# 线程thread
一、问题总结
1. 在WinForm开发过程中用到线程时,往往需要在线程中访问线程外的控件,比如:设置textbox的Text值等等。如果直接访问UI控件会报出“从不是创建控件的线程访问它”错误。控件是在主线程中创建的(比如this.Controls.Add(...);),在其它线程直接访问主线程控件,与主线程发生线程冲突。
解决方法:
在控件响应函数中调用控件的Invoke方法,Invoke方法会顺着控件树向上搜索,直到找到创建控件的那个线程(通常是主线程),然后进入那个线程改变控件的外观,确保不发生线程冲突。
MSDN中说:
获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。
如果控件的 Handle 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke 方法对控件进行调用),则为 true;否则为 false。
Windows 窗体中的控件被绑定到特定的线程,不具备线程安全性 。因此,如果从另一个线程调用控件的方法,那么必须使用控件的一个 Invoke 方法来将调用封送到适当的线程。该属性可用于确定是否必须调用 Invoke 方法,当不知道什么线程拥有控件时这很有用。
2. invoke表示同步,begininvoke表示异步。
3.在使用invoke前,判断IsHandleCreated 此属性指示控件是否有与他关联的句柄,如果已经为控件分配了句柄,则为 true;否则为 false。
加此判断可以避免在退出程序的时候,如果此时调用了invoke中的控件,就会出现错误“在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。”
二、代码举例
1.线程访问主线程控件
using System; using System.Windows.Forms; using System.Threading; namespace WindowsFormsApp1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } private void btnStart_Click(object sender, EventArgs e) { Thread th = new Thread(new ThreadStart(DoWork)); th.Start(); } private void DoWork() { ShowMsg("hello world"); } delegate void ShowMsgDelegate(string msg); //普通委托 private void ShowMsg(string msg) { if(lblMsg.InvokeRequired) { ShowMsgDelegate showMsgDelegate = new ShowMsgDelegate(ShowMsg); lblMsg.Invoke(showMsgDelegate, new object[] { msg }); } else { lblMsg.Text = msg; } } //匿名代理 private void ShowMsg2(string msg) { ShowMsgDelegate showMsgDelegate = delegate (string str) { lblMsg.Text = str; }; lblMsg.Invoke(showMsgDelegate, new object[] { msg }); } //在C# 3.0及以后的版本中有了Lamda表达式, //像上面这种匿名委托有了更简洁的写法。 private void ShowMsg3(string msg) { this.Invoke(new Action(() => { lblMsg.Text = msg; } )); } } }
private void ShowMsg(string msg, bool isOk) { if (this.IsHandleCreated) { this.Invoke(new Action(() => { lblMsg.Text = msg; lblMsg.ForeColor = isOk ? Color.Green : Color.Red; })); } }
lambda写法
private void button1_Click(object sender, EventArgs e) { Console.WriteLine("start"); testThread =new Thread(() =>{ for(int i=0;i<20;i++) { Console.WriteLine(i); } }); testThread.Start(); Console.WriteLine("end"); }
执行结果:
start
end
0
1
2
3
4
5
6
7
8
9