什么是线程池?
为什么要用线程池?
前台线程&后台线程
主要用来完成 输入和输出的工作 ,在这种情况下, 计算机需要I/O设备完成输入和输出的任务。在处理过程中, CPU是不需要参与处理过程的 , 此时正在运行的线程将处于等待状态 , 只有等任务完成后才会有事可做 , 这样就造成线程资源浪费的问题。为了解决这样的问题,可以通过线程池来解决这样的问题,让线程池来管理线程
.NET中的一些API方法,通过APM(异步编程模式),内部实现了ThreadPool.BindHandle方法。BeginXXX方法将用户的回调委托送到某个设备驱动程序,然后返回线程池。
当做完成后,OS会通过IOCP提醒CLR它工作已经完成,当接收到通知后,I/O线程会醒来并且运行用户的回调
所以工作线程由开发人员调用, I/O线程由CLR调用 。所以通常情况下,开发者并不会直接用到它。因此可以认为, 工作者线程和I/O线程没有区别,它们都是普通的线程 , 但是CLR线程池中区分它们的目的是为了避免线程都去处理I/O回调而被耗尽,从而引发死锁 。(设想,所有的工作者线程每一个都去等待I/O异步完成。)
用来完成一些 计算的任务 ,在任务执行的过程中,需要 CPU不间断地处理 ,所以,在工作者线程的执行过程中,CPU和线程的资源是充分利用的
.NET中的术语工作者线程指的是任何线程而不是仅仅主线程
工作者线程
I/O线程
static bool QueueUserWorkItem(WaitCallback callBack)
:参数为一个带一个object
类型参数的委托,最后返回bool
值成功则返回true
class ThreadPoolTest
{
static void Main()
{
Console.WriteLine("启动多线程...");
for(int i = 0; i <10; i++)
{
ThreadPool.QueueUserWorkItem( p => printStr("当前线程") );
}
Console.WriteLine("结束多线程...");
Console.ReadKey();
}
private static void printStr(string str)
{
Console.WriteLine("{0}是:{1}", str, Thread.CurrentThread.ManagedThreadId);
}
}
输出结果, 可以看到有很多线程ID是重复的,这就是线程池的强大之处了
启动多线程...
结束多线程...
当前线程是:8
当前线程是:6
当前线程是:10
当前线程是:9
当前线程是:7
当前线程是:11
当前线程是:12
当前线程是:9
当前线程是:6
当前线程是:10
查看最大/最小线程数 && 设置最大/最小线程数
class ThreadPoolTest
{
static void Main()
{
// 获取默认的线程池中的最大,最小线程数
ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads);
Console.WriteLine("最大线程数,工作线程:{0}, IO线程数:{1}", maxWorkerThreads, maxCompletionPortThreads);
ThreadPool.GetMinThreads(out int minWorkerThreads, out int minCompletionPortThreads);
Console.WriteLine("最小线程数,工作线程:{0}, IO线程数:{1}", minWorkerThreads, minCompletionPortThreads);
// 重新设置最大、最小线程数
ThreadPool.SetMaxThreads(10, 10);
ThreadPool.SetMinThreads(3, 3);
// 获取默认的线程池中的最大,最小线程数
ThreadPool.GetMaxThreads(out int newMaxWorkThread, out int newMaxIOThread);
Console.WriteLine("重新设置后的最大线程数,工作线程:{0}, IO线程数:{1}", newMaxWorkThread, newMaxIOThread);
ThreadPool.GetMinThreads(out int newMinWorkThread, out int newMinIOThread);
Console.WriteLine("重新设置后的最小线程数,工作线程:{0}, IO线程数:{1}", newMinWorkThread, newMinIOThread);
Console.WriteLine("启动多线程...");
for(int i = 0; i <10; i++)
{
ThreadPool.QueueUserWorkItem( p => printStr("当前线程") );
}
Console.WriteLine("结束多线程...");
Console.ReadKey();
}
private static void printStr(string str)
{
Console.WriteLine("{0}是:{1}", str, Thread.CurrentThread.ManagedThreadId);
}
}
输出结果
最大线程数,工作线程:32767, IO线程数:1000
最小线程数,工作线程:8, IO线程数:8
重新设置后的最大线程数,工作线程:10, IO线程数:10
重新设置后的最小线程数,工作线程:3, IO线程数:3
启动多线程...
结束多线程...
当前线程是:6
当前线程是:9
当前线程是:7
当前线程是:8
当前线程是:11
当前线程是:10
当前线程是:12
当前线程是:11
当前线程是:7
当前线程是:9
查看系统cpu的处理数
编辑
需要使用到ManualResetEvent类
**ManualResetEvent需要一个bool类型的参数来表示暂停和停止
**
class ThreadPoolTest2
{
// 参数设置为false
static ManualResetEvent manualResetEvent = new ManualResetEvent(false);
static void Main()
{
Console.WriteLine("启动多线程...");
ThreadPool.QueueUserWorkItem(p => printStr("当前线程"));
// 等待 线程池执行任务完成
manualResetEvent.WaitOne();
Console.WriteLine("结束多线程...");
}
private static void printStr(string str)
{
Console.WriteLine("{0}是:{1}", str, Thread.CurrentThread.ManagedThreadId);
// 设置为true
manualResetEvent.Set();
}
}
输出结果
启动多线程...
当前线程是:6
结束多线程...
注:一般情况下,不要阻塞线程池中的线程,因为写代码无意间造成的线程等待没有释放,一旦线程池线程耗尽就会形成死锁
造成死锁的案例: 设置了最大线程数是9,循环创建15个线程,意味着 后6个线程必然需要利用线程池中前面用过的线程, 但是由于前面创建的线程都直接让阻塞了,没有释放,这时循环到第10个线程时由于没有空闲线程,线程池没法执行就直接跳过了,主线程会继续执行后面的循环,这样也永远不会 执行 manualResetEvent.Set() 方法,最后就成死锁了
private static void Test1()
{
// 设置最大线程
ThreadPool.SetMaxThreads(9, 9);
// 设置最小线程
ThreadPool.SetMinThreads(3, 3);
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
for (int i = 0; i < 15; i++)
{
int k = i;
ThreadPool.QueueUserWorkItem(p =>
{
Console.WriteLine(k);
if (k < 10)
{
manualResetEvent.WaitOne();
}
else
{
// 设为true
manualResetEvent.Set();
}
});
}
if (manualResetEvent.WaitOne())
{
Console.WriteLine("没有死锁。。。。。。。。。");
}
Console.WriteLine("结束。。。。。。。。。。");
}
输出结果
1
0
2
3
4
5
6
7
8
全部0条评论
快来发表一下你的评论吧 !