在C#编程语言中,使用线程池能够并行地处理工做,当强制线程和更新进度条时,会使用内建架构的ThreadPool类,为批处理使用多核结构,这里咱们来看在C#编程语言中一些关于来自System.Threading的ThreadPool的用法的例子。web
介绍编程
.NET Framework提供了包含ThreadPool类的System.Threading 空间,这是一个可直接访问的静态类,该类对线程池是必不可少的。它是公共“线程池”设计样式的实现。对于后台运行许多各不相同的任务是有用的。对于单个的后台线种而言有更好的选项。服务器
线程的最大数量。这是彻底无须知道的。在.NET中ThreadPool的全部要点是它本身在内部管理线程池中线程。多核机器将比以往的机器有更多的线程。微软如此陈述“线程池一般有一个线程的最大数量,若是全部的线程都忙,增长的任务被放置在队列中直到它们能被服务,才能做为可用的线程。”网络
用法位置架构
线程池类型能被用于服务器和批处理应用程序中,线程池有更廉价的获得线程的内部逻辑,由于当须要时这些线程已被造成和恰好“链接”,因此线程池风格代码被用在服务器上。异步
MSDN表述:“线程池常常用在服务器应用程序中,每个新进来的需求被分配给一个线程池中的线程,这样该需求能被异步的执行,没有阻碍主线程或推迟后继需求的处理。”编程语言
MSDN 参考工具
ThreadPool VS BackgroundWorker性能
若是你正在使用Windows窗体,宁肯使用BackgroundWorker来对付那些更简单的线程需求,BackgroundWorker在网络访问和其余一些简单的事情方面作得很好。但对于多处理器的批处理来讲,你须要ThreadPool。this
当你的程序要批处理时,考虑线程池
当你的程序产生不少(3个以上)线程时,考虑线程池
当你的程序使用Windows窗体时,考虑后台执行。
线程要考虑的事 一样,如何使用线程的细节能帮助发现最好的代码。下面比较线程情形和哪一个类是最好的。
你须要一个额外的线程 使用后台执行
你有许多短时间的线程 使用线程池
需求
线程很重要,但对于那些不会花很长时间来执行且只作一件事情的大多数应用程序来讲却并不重要的。线程对于界面可用性不是很重要的的应用程序而言也不是很重要,要尽可能避免使用线程(译者注:好比进度条不是很重要的应用程序)。
链接方法
可以使用QueueUserWorkItem链接方法(methods)到线程池。方法要运行在线程上,则必须把它链接到QueueUserWorkItem。如何实现呢?必须使用WaitCallback。在MSDN中,WaitCallback被描述成当线程池执行时要被调用的委托回调方法,是回调它的参数的委托。
WaitCallback
只需指定“new WaitCallback”语句做为ThreadPool.QueueUserWorkItem的第一个参数来使用WaitCallback.不须要任何其余的代码来使用这方法生效。
使用WaitCallback的例子
void Example() { // 链接 ProcessFile 方法到线程池. //注意: 'a' 是一个做为参数的对象 ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), a); } private void ProcessFile(object a) { // 我被链接到线程池经过 WaitCallback. }
参数
咱们能经过定义一个特定的类并把一些重要的值放在类里面来使用参数,那么,方法接收了对象,就能经过对象向方法传递多个参数了。如下是一个早期的例子。
使用带参数QueueUserWorkItem 的例子
//指定做为线程池方法的参数的类 class ThreadInfo { public string FileName { get; set; } public int SelectedIndex { get; set; } } class Example { public Example() { // 声明一个新的参数对象 ThreadInfo threadInfo = new ThreadInfo(); threadInfo.FileName = "file.txt"; threadInfo.SelectedIndex = 3; //发送自定义的对象到线程方法 ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), threadInfo); } private void ProcessFile(object a) { ThreadInfo threadInfo = a as ThreadInfo; string fileName = threadInfo.FileName; int index = thread.SelectedIndex; } }
发生了什么事?咱们发送两个值给这个线程化的ProcessFile方法,它须要知道文件名和选择索引,而咱们在这个对象中把参数都发送了给它。
进度条
能经过从设计器中右边的工具盒面板中增长Windows窗体控件到你的窗体程序来使用进度条并设置 progressBar1.Value, progressBar1.Minimum 和progressBar1.Maximum。 progressBar1.Value是最小值和最大值中间的位置,如下代码用来初始化进度条:
设置进度条的例子
//设置进度条的长度. // 这里咱们有6个单位来完成,因此6是最大值。 // 最小值一般是0 progressBar1.Maximum = 6; // 或其余数字 progressBar1.Minimum = 0;
进度条位置 你的进度条中的有颜色部分是当前值与最大值的百分比。因此,若是最大值是6,而值是3即表示作完了一半。
ProgressBar 例子 (Windows Forms)
在进度条中调用Invoke(援引)
让咱们看如何在进度条实例中使用Invoke方法。遗憾的是,你不能在辅助线程中访问Windows控件,由于UI线程是分离的,必须使用委托(delegate)和Invoke到进度条。
请求Invoke(调用)的例子
public partial class MainWindow : Form { // 这是运行在UI线程来更新条的委托 public delegate void BarDelegate(); //该窗体的构造器(由Visual Studio本身产生) public MainWindow() { InitializeComponent(); } //当按下按钮,启动一个新的线程 private void button_Click(object sender, EventArgs e) { // 设定进度条的长度. progressBar1.Maximum = 6; progressBar1.Minimum = 0; // 给线程传递这些值. ThreadInfo threadInfo = new ThreadInfo(); threadInfo.FileName = "file.txt"; threadInfo.SelectedIndex = 3; ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), threadInfo); } // 运行在后台线程上的东西 private void ProcessFile(object a) { // (省略) // 使用'a'作一些重要的事. // 告诉UI 咱们已经完成了. try { // 在窗体中调用委托 . this.Invoke(new BarDelegate(UpdateBar)); } catch { //当一些问题发生后咱们能使程序恢复正常 } } //更新进度条. private void UpdateBar() { progressBar1.Value++; if (progressBar1.Value == progressBar1.Maximum) { // 结束了,进度条满了. } } }
委托语法 以上代码的开始处,能够看到声明 UpdateBar 的委托。它告诉Visual Studio 和C# 须要来使用这个做为对象的方法。
更多须要的工做 以上程序演示了如何设定进度条的最大值和最小值,如何在工做完成后“Invoke”委托方法来增长进度条的大小。
在调试器中的线程
这儿要显示如何在Visual Studio的调试器中查看线程。一旦你有一个运行的程序,你能采起这些措施来可视化你的线程。首先,以调试模式打开你的线程应用程序,一旦你的应用程序运行在调试器,告知它去作它的工做并且运行这些线程,经过绿色箭头运行调试器,当线程正在运行,在工具条中单击“pause"按钮。
下一步 调试>窗口>线程.该菜单项将打开一个相似下图的窗口,你能看见有多少线程正在线程池中运行。
四个辅助线程 上图显示了共有10个线程,但只有四个辅助线程(Worker Thread)在程序中被分配给MainWindow.ProcessFile.
约束辅助线程
若是你有一个双核或四核系统,你将考虑最多两个四个很费力的线程。咱们能在运行的线程中保持一个_threadCount 字段而且跟踪它的数值。用这个线程计数字段,你将须要在C#语言中使用一个锁来避免形成这个字段读和写的错误,锁保护你的线程被其余线程所改变。
计数线程的例子
// 锁住这个对象. readonly object _countLock = new object(); private void ProcessFile(object argument) { // 约束辅助线程的数量 while (true) { lock (_countLock) { if (_threadCount < 4) { // Start the processing _threadCount++; break; } } Thread.Sleep(50); } // Do work... }
咱们看到什么 以是代码是异步执行的方法。只有其余辅助线程少于4个时它才会工做。这对于一个四核机器是好的。请看描述锁声明的更多上下文的文章
控制线程计数器
你能够在ThreadPool上使用SetMinThreads 来在连发活动中提升吞吐量和性能。如下是关于可以使用的最佳的最小线程数量的材料。
ThreadPool.SetMinThreads Method
总结
咱们了解了如何在C#程序中使用线程池来有效管理多个线程,在Windows 窗体应用程序的进度条和用户界面中能给人留很深印象而且也不难实现。然而,线程带来了不少的复杂性并致使漏洞,线程池是一个有用的简化,但它仍然是困难的。