前面我们介绍了Thread类的使用,此类在。NET1.0版本的时候就有了,其内部含有大量的方法和属性。使用起来略有繁琐,为了提高使用多线程的效率,杜绝滥用多线程,.NET提出了ThreadPool也就是线程池的概念。
何为ThreadPool线程池?
实际上就是专门放置线程的池子,它是一个类,此类分装了Thread类中的一些方法和属性,使用户使用起来更加的快捷。此外它的使用效率高,需要的时候从池子里面分配线程,不用的话销毁还原线程。如此便能够提升资源,提高效率。
一、ThreadPool类
ThreadPool类中有比较多的方法和属性。其中使用较为频繁的如下:
public static bool QueueUserWorkItem(WaitCallback callBack); //方法入队线程池,排队进入成功则返回true,否则为false并有异常
public static bool QueueUserWorkItem(WaitCallback callBack, object state); //与上述方法同理,state为要相关联的数据
public static void GetMaxThreads(out int workerThreads, out int completionPortThreads); //获取最大线程数,包括woeker辅助线程和completionPort异步IO线程
public static void GetMinThreads(out int workerThreads, out int completionPortThreads); //获取最小线程数
public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads); //获取当前有效可用的线程数
public static bool SetMaxThreads(int workerThreads, int completionPortThreads); //设置最大线程数,注意不能比CPU核数小,否则设置不成功
public static bool SetMinThreads(int workerThreads, int completionPortThreads); //设置最小线程数,注意不能比CPU核数小,否则设置不成功
如下代码:
int workerThreads=0,completionPortThreads=0;
ThreadPool.GetMaxThreads(out workerThreads,out completionPortThreads);
Console.WriteLine("当前线程池最大workerTreads数目为{0},最大conpletionPortThreads数目为{1}",workerThreads,completionPortThreads);//1023,1000
ThreadPool.GetMinThreads(out workerThreads,out completionPortThreads);
Console.WriteLine("当前线程池最小workerTreads数目为{0},最小conpletionPortThreads数目为{1}", workerThreads, completionPortThreads);//4,4
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads); //返回可用的线程数目,辅助线程和异步IO数目
Console.WriteLine("当前线程池可用workerTreads数目为{0},可用conpletionPortThreads数目为{1}", workerThreads, completionPortThreads);//4,4
//设置的线程池的线程数量是进程全局的,
//委托异步调用--Task--Parrallel--async/await 全部都是线程池的线程
//直接new Thread不受这个数量限制的(但是会占用线程池的线程数量)
ThreadPool.SetMaxThreads(8,8); //可以设置最大线程,分为worker线程和completionPort线程,数值不能小于CPU核数否无效
ThreadPool.SetMinThreads(2,2); //可以设置最小线程,同样分为worker和conpletionPort线程
输出结果为:
二、线程池开启与方法入队
线程池中实际上是有队列的,队列中存放的是排队的待执行的方法,线程池会调度这些方法在不同的线程中执行已达到效率最大化。
下面演示如何开启线程池和方法入队。
#region //两个共用方法用作调用
public void doSomething1(string name) //定义一个方法名为doSomething1()
{
Console.WriteLine("doSometing1方法调用开始");
Thread.Sleep(2000); //将当前的线程暂停2000ms即2秒
//输出调用方名称+当前线程名+本方法标识
Console.WriteLine("用:" + name + "在" + Thread.CurrentThread.ManagedThreadId.ToString("00") + "线程中执行方法1\n");
}
public void doSomething2(string name) //定义一个方法名为doSomething2()
{
Console.WriteLine("doSometing2方法调用开始");
Thread.Sleep(2000); //将当前的线程暂停2000ms即2秒
//输出调用方名称+当前线程名+本方法标识
Console.WriteLine("用:" + name + "在" + Thread.CurrentThread.ManagedThreadId.ToString("00") + "线程中执行方法2\n");
}
#endregion
接着将项目的输出设置为控制台输出:
输入以下代码:
#region //ThreadPool基本使用
private void button1_Click(object sender, EventArgs e) //绑定按钮点击事件
{
Console.WriteLine("线程池演示开始,当前主线程为:"+Thread.CurrentThread.ManagedThreadId);
#region //如何开启线程
//QueueUserWorkItem()方法接受的参数为WaitCallback委托,他的定义为:public delegate void WaitCallback(object state);
ThreadPool.QueueUserWorkItem(o => this.doSomething1("第1个进入线程池队列的方法")); //参数为WaitCallback委托,它是有参的
ThreadPool.QueueUserWorkItem(o=>this.doSomething2("第2个进入线程池队列的方法"),"C#自学"); //调用QueueUserWorkItem()方法的两个参数重载方法
Console.WriteLine("两个方法已经进入线程池里");
#endregion
}
#endregion
上述代码点击运行后,结果如下:
从上面的结果我们可以看出,由于多线程的作用,主线程没有阻塞,首先输出“线程池演示开始,当前主线程为:9”,接着两个方法入队,输出“两个方法已经进入线程池里”,此时调用主线程执行完毕,线程编号为9,接着线程池里会分配线程执行入队的两个委托,一个在线程10里,一个在线程11里。两个并行处理,最终结束。
三、ThreadPool等待
前面提到过Thread线程等待使用Join()方法可以实现,或者使用while循环判断thread.ThreadState的状态值是否为ThreadState.Stopped,那么线程池中的等待不同。具体如下:
#region
private void button2_Click(object sender, EventArgs e)
{
//public ManualResetEvent(bool initialState); //如果initialState如果为true,则将初始状态设置为终止;如果为 false,则将初始状态设置为非终止。
ManualResetEvent mre = new ManualResetEvent(false);
//false---关闭,Set()打开---true---WaitOne就能通过
//true--打开--ReSet关闭--false---WaitOne就只能等待
//等待方式2:bool Isfinish = false; //也可以使用这种比较简单的方式
ThreadPool.QueueUserWorkItem(o=> //多线程针对的是方法来说的,对于一个方法内部的语句执行顺序仍然是按顺序来的
{
doSomething1("ThreadPool线程池中等待");
mre.Set(); //开启,下面的mre.WaitOne就会等待完成,后才能执行最后的代码
Console.WriteLine("1");
Console.WriteLine("2");
Console.WriteLine("3");
Console.WriteLine("4");
//等待方式2:Isfinish = true;
});
//等待方式2:while(!Isfinish); //直接判断Isfinish是否为true就行了。
mre.WaitOne(); //WaitOne()方法依赖于mre是否为true,为true才有效,会等待,否则无效不等待
//mre.WaitOne(2000); //最多等待2秒,超过不等
Console.WriteLine("执行完毕");
}
#endregion
执行结果如下:
由上面的结果我们可以开出,由于WaitOne的作用,且mre为true使之线程等待开启,所以一开始等待被调函数的顺序执行完毕后才执行在“执行完毕”