多线程,线程池与BeginInvoke()
在WinForm中,很多情况下需要用到多线程,下面我来简单介绍一下多线程的基本用法。
1.线程。
(1)线程的初始化
Thread t = new Thread(new ThreadStart(ThreadProc));
其中,ThreadStart是一个系统定义的委托,ThreadProc是一个方法的名称,其签名与ThreadStart一样。
线程的启动:
(2)t.Start();
这样子,ThreadProc方法就会执行了。t 默认是一个非主线程,并且是一个前台线程。
(3)主线程等待子线程结束后再继续运行:
t.Join();
这个方法的意思是,主线程暂时挂起,等待线程t运行结束以后,主线程再激活,继续运行。
(4)当前线程休眠:
System.Threading.Thread.Sleep(time); //time表示当前线程挂起的毫秒数。
2.线程池。
由于新增线程的开销是非常大的,所以,如果这个任务不是十分紧急且重要的话,不妨使用CRL线程池。CRL线程池是 .net 的一种分配线程的机制。它会根据任务量自动分配线程,并且是线程的利用率最大化,从而减小性能开销。
(1)想线程池队列中添加任务:
ThreadPool.QueueUserWorkItem(new WaitCallback(myFunction));
其中,QueueUserWorkItem方法是ThreadPool的静态方法,其作用是向当前进程的线程池队列中添加一个任务,他的参数是一个WaitCallBack委托的对象。WaitCallBack是系统定义的一个委托,其签名如下,
public delegate void WaitCallback(object state);
这就将MyFunction这个方法作为一个线程,添加到了线程池中,等待线程池的调用。
3.Invoke() 和 BeginInvoke() 方法。
大家都知道,不同的线程之间,程序和数据时互不干扰的。但是,在WinForm中,如果其他线程想要在执行完毕后,将其结果显示到UI界面上的话,程序就会出现异常,大概意思就是说,某控件不是在该线程中创建的,所以该线程不能访问器句柄(即对象,本质是一个指针)。那么,怎么解决这个问题呢?其实,这些问题,微软早就考虑到了。Control类中有Invoke() 和 BeginInvoke() 方法,他们就是用来处理其他线程访问UI线程数据的。我现在给大家写一个例子,让界面的一个label一直显示当前时间,并不断刷新。
首先,打开一个WinForm程序,往窗体上拖一个label。
然后添加命名空间 Using System.Threading ;
在把下面的代码写到Form1.cs中
/// <summary> /// 这个方法实现更新label,让它显示当前时间 /// </summary> /// <param name="o"> 这里的这个参数没有意义,只是为了符合某个委托的签名</param> private void ShowTime(object o) { while (true) { BeginInvoke(new Action(() => { label1.Text = "当前时间:" + DateTime.Now.ToString(); })); Thread.Sleep(1000); } }
但是,怎么让label一直更新呢,这就需要再开一个线程,我们不妨利用线程池。
我们注册一个Form1的Load事件,然后在这个事件中写下下一行代码:
ThreadPool.QueueUserWorkItem(new WaitCallback(ShowTime));
好了,下面这个功能就做好了。
下面,我解释一下里面的一些东西。
BeginInvoke是Control类的一个方法,其参数是一个WaitCallBack委托的对象。
new Action(() => { label1.Text = "当前时间:" + DateTime.Now.ToString(); }),这是一个匿名方法,Action的参数是一个Lambda表达式。Action是系统定义的一个委托,其签名如下:
public delegate void Action();即空返回值,空参数。