线程池,千万注意,原来很多人都在错用
2016-10-28 22:12
405 查看
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行。
[C#线程池]
场景:以下是C# winform写的一个线程池示例程序。窗体上,分别拖放一个开始和停止按钮,单击开始按钮,for寻觅模拟7000个任务队列,在线程池运行过程中,可以点击停止按钮,来结束线程池所有任务,这里借助CancellationTokenSource对象,来实现线程池的停止。
![](https://static.oschina.net/uploads/space/2016/1028/212831_yS7O_584165.png)
开始前,请大家看以下代码,有什么问题?这里以C# Winform的形式,向大家展现以下代码:
1、第一种:这是开始按钮单击事件的代码
2、第二种:看出有何不同了吗,好像差不多,都很正常对吧
3、第三种:
如果没有看出以上三段代码有何不同,那就一定要看看下面的内容。
接下来,我们借助操作系统的任务管理器,来及时监测Winform进程所进行的线程数。
如果,是第一种写法,我们来看看:
![](https://static.oschina.net/uploads/space/2016/1028/213220_O5Mi_584165.png)
结果,并不像我们预想的那样,即使如此,线程数还在一路飙升,不一会儿竟达到两百多个线程,这是为何?
第二种,只不过是比第一种少些了一行System.Threading.ThreadPool.SetMinThreads(2, 1);的代码,结果,
好像没有什么变化,线程池仍然一路飙升,直到UI线程出现假死。
就在我写这两段博客的瞬间,看看任务管理器的情况吧:
![](https://static.oschina.net/uploads/space/2016/1028/214314_AXrg_584165.png)
什么,线程数601?暂不说它是否精确,但此刻,程序是很不正常的在运行。
第三种,是在第一种基础上,调整了两行代码的顺序,恰是这两行代码,让我们正视了之前线程池误区。也改变了结果。以第三种方式,我将程序运行6个多小时,一夜不关,程序依然运行平稳,线程数依然保持在较稳的水平。
原来,顺序真的很重要。经常看到网上有关C#线程池用法的博客,当我们看到,上来都写System.Threading.ThreadPool.SetMaxThreads(4, 2);这行代码时,我们切记要注意了。当任务量小的时候,这种问题并不明显,依然能很好的完成工作。但是,像我以上的例子,庞大任务队列加入线程池,每个任务又以漫长的运行周期在运行。那把上面那个例子,搬上去看看,这种写法必死,切记。
使用C#线程池的时候,一定要先设置 System.Threading.ThreadPool.SetMinThreads(2, 1);,然后再设置 System.Threading.ThreadPool.SetMaxThreads(4, 2);,这才是C#线程池正确的用法。
最后给大家介绍一个关键词volatile,不知道的朋友,可以了解下。想要了解更多多线程池方面的信息,请访问http://www.lichaoqiang.com/
[C#线程池]
场景:以下是C# winform写的一个线程池示例程序。窗体上,分别拖放一个开始和停止按钮,单击开始按钮,for寻觅模拟7000个任务队列,在线程池运行过程中,可以点击停止按钮,来结束线程池所有任务,这里借助CancellationTokenSource对象,来实现线程池的停止。
![](https://static.oschina.net/uploads/space/2016/1028/212831_yS7O_584165.png)
开始前,请大家看以下代码,有什么问题?这里以C# Winform的形式,向大家展现以下代码:
1、第一种:这是开始按钮单击事件的代码
btnStart.Enabled = false; isStop = false; listCancels.Clear();//清空 cancelToken.Dispose(); cancelToken = new CancellationTokenSource(); System.Threading.ThreadPool.SetMaxThreads(4, 2); System.Threading.ThreadPool.SetMinThreads(2, 1); for (int i = 0; i < 7000; i++) { System.Threading.ThreadPool.QueueUserWorkItem((state) => { while (true) { lock (locker) { if (isStop) { break; }; if (cancelToken.IsCancellationRequested) { break; } } int works = 0, maxwork = 0; int port = 0, maxport = 0; System.Threading.ThreadPool.GetAvailableThreads(out works, out port); System.Threading.ThreadPool.GetMaxThreads(out maxwork, out maxport); lblStatus.BeginInvoke(new Action(() => { lblStatus.Text = System.Threading.Thread.CurrentThread.ManagedThreadId + ":" + Guid.NewGuid(); })); System.Threading.Thread.Sleep(100); } }); }
2、第二种:看出有何不同了吗,好像差不多,都很正常对吧
btnStart.Enabled = false; isStop = false; listCancels.Clear();//清空 cancelToken.Dispose(); cancelToken = new CancellationTokenSource(); System.Threading.ThreadPool.SetMaxThreads(4, 2); for (int i = 0; i < 7000; i++) { System.Threading.ThreadPool.QueueUserWorkItem((state) => { while (true) { lock (locker) { if (isStop) { break; }; if (cancelToken.IsCancellationRequested) { break; } } int works = 0, maxwork = 0; int port = 0, maxport = 0; System.Threading.ThreadPool.GetAvailableThreads(out works, out port); System.Threading.ThreadPool.GetMaxThreads(out maxwork, out maxport); lblStatus.BeginInvoke(new Action(() => { lblStatus.Text = System.Threading.Thread.CurrentThread.ManagedThreadId + ":" + Guid.NewGuid(); })); System.Threading.Thread.Sleep(100); } }); }
3、第三种:
btnStart.Enabled = false; isStop = false; listCancels.Clear();//清空 cancelToken.Dispose(); cancelToken = new CancellationTokenSource(); System.Threading.ThreadPool.SetMinThreads(2, 1); System.Threading.ThreadPool.SetMaxThreads(4, 2); for (int i = 0; i < 7000; i++) { System.Threading.ThreadPool.QueueUserWorkItem((state) => { while (true) { lock (locker) { if (isStop) { break; }; if (cancelToken.IsCancellationRequested) { break; } } int works = 0, maxwork = 0; int port = 0, maxport = 0; System.Threading.ThreadPool.GetAvailableThreads(out works, out port); System.Threading.ThreadPool.GetMaxThreads(out maxwork, out maxport); lblStatus.BeginInvoke(new Action(() => { lblStatus.Text = System.Threading.Thread.CurrentThread.ManagedThreadId + ":" + Guid.NewGuid(); })); System.Threading.Thread.Sleep(100); } }); }
如果没有看出以上三段代码有何不同,那就一定要看看下面的内容。
接下来,我们借助操作系统的任务管理器,来及时监测Winform进程所进行的线程数。
如果,是第一种写法,我们来看看:
![](https://static.oschina.net/uploads/space/2016/1028/213220_O5Mi_584165.png)
结果,并不像我们预想的那样,即使如此,线程数还在一路飙升,不一会儿竟达到两百多个线程,这是为何?
第二种,只不过是比第一种少些了一行System.Threading.ThreadPool.SetMinThreads(2, 1);的代码,结果,
好像没有什么变化,线程池仍然一路飙升,直到UI线程出现假死。
就在我写这两段博客的瞬间,看看任务管理器的情况吧:
![](https://static.oschina.net/uploads/space/2016/1028/214314_AXrg_584165.png)
什么,线程数601?暂不说它是否精确,但此刻,程序是很不正常的在运行。
第三种,是在第一种基础上,调整了两行代码的顺序,恰是这两行代码,让我们正视了之前线程池误区。也改变了结果。以第三种方式,我将程序运行6个多小时,一夜不关,程序依然运行平稳,线程数依然保持在较稳的水平。
原来,顺序真的很重要。经常看到网上有关C#线程池用法的博客,当我们看到,上来都写System.Threading.ThreadPool.SetMaxThreads(4, 2);这行代码时,我们切记要注意了。当任务量小的时候,这种问题并不明显,依然能很好的完成工作。但是,像我以上的例子,庞大任务队列加入线程池,每个任务又以漫长的运行周期在运行。那把上面那个例子,搬上去看看,这种写法必死,切记。
使用C#线程池的时候,一定要先设置 System.Threading.ThreadPool.SetMinThreads(2, 1);,然后再设置 System.Threading.ThreadPool.SetMaxThreads(4, 2);,这才是C#线程池正确的用法。
最后给大家介绍一个关键词volatile,不知道的朋友,可以了解下。想要了解更多多线程池方面的信息,请访问http://www.lichaoqiang.com/
相关文章推荐
- 删除内存树结构的时候,千万注意内存泄漏问题,采用递归比较简单。
- 注意!这13件事会毁掉你的信用,千万别做!
- 线程池 ExecutorService 详细介绍以及注意点区别
- UESTC 58 任意阶矩阵的乘法 虽然简单但优化还是要思考一下的 而且也使自己意识到了原来没有注意的问题
- 职场上千万要注意这两个小细节
- 线程池使用拒绝策略时需要注意的坑
- 千万要注意spring中的命名规范
- juqery,ajax跨域,千万注意
- 线程池 ExecutorService 详细介绍以及注意点区别
- 在使用线程池时应特别注意对ThreadLocal的使用
- 原来现在很多人都用SignalR来实现Chat Room
- 线程池 ExecutorService 详细介绍以及注意点区别
- buffer的slice()方法返回的新buffer和原buffer引用的是同一个对象!!!修改千万要注意!
- ThreadLocal和线程池使用要注意的一个地方
- TLC1543交流电压测试(单片机)教训版(千万注意电压型电路供流能力问题,注意器件属于电压输入型还是电流输入型)
- 八种菜隔夜万万吃不得!尤其是第三种,千万要注意!
- Threadlocal的几种误区(尤其线程池中使用时注意)
- EL表达式(------使用时千万要注意------)
- 在线程池(ExecutorService)中使用Threadlocal时,需要注意他的复位操作
- 浅谈线程池(下):相关试验及注意事项