C#线程池用法详细介绍

 更新时间:2020年6月25日 11:39  点击:2338

介绍

.NET Framework提供了包含ThreadPool类的System.Threading 空间,这是一个可直接访问的静态类,该类对线程池是必不可少的。它是公共“线程池”设计样式的实现。对于后台运行许多各不相同的任务是有用的。对于单个的后台线种而言有更好的选项。

线程的最大数量。这是完全无须知道的。在.NET中ThreadPool的所有要点是它自己在内部管理线程池中线程。多核机器将比以往的机器有更多的线程。微软如此陈述“线程池通常有一个线程的最大数量,如果所有的线程都忙,增加的任务被放置在队列中直到它们能被服务,才能作为可用的线程。”

用法位置

线程池类型能被用于服务器和批处理应用程序中,线程池有更廉价的得到线程的内部逻辑,因为当需要时这些线程已被形成和刚好“连接”,所以线程池风格代码被用在服务器上。

MSDN表述:“线程池经常用在服务器应用程序中,每一个新进来的需求被分配给一个线程池中的线程,这样该需求能被异步的执行,没有阻碍主线程或推迟后继需求的处理。”

MSDN 参考

ThreadPool  VS  BackgroundWorker

如果你正在使用Windows窗体,宁可使用BackgroundWorker来对付那些更简单的线程需求,BackgroundWorker在网络访问和其他一些简单的事情方面做得很好。但对于多处理器的批处理来说,你需要ThreadPool。

BackgroundWorker 教程

当你的程序要批处理时,考虑线程池

当你的程序产生很多(3个以上)线程时,考虑线程池

当你的程序使用Windows窗体时,考虑后台执行。

线程要考虑的事 同样,如何使用线程的细节能帮助发现最好的代码。下面比较线程情形和哪个类是最好的。

   你需要一个额外的线程   使用后台执行

   你有许多短期的线程     使用线程池

需求

线程很重要,但对于那些不会花很长时间来执行且只做一件事情的大多数应用程序来说却并不重要的。线程对于界面可用性不是很重要的的应用程序而言也不是很重要,要尽量避免使用线程(译者注:比如进度条不是很重要的应用程序)。

连接方法

可使用QueueUserWorkItem连接方法(methods)到线程池。方法要运行在线程上,则必须把它连接到QueueUserWorkItem。如何实现呢?必须使用WaitCallback。在MSDN中,WaitCallback被描述成当线程池执行时要被调用的委托回调方法,是回调它的参数的委托。

WaitCallback

只需指定“new WaitCallback”语句作为ThreadPool.QueueUserWorkItem的第一个参数来使用WaitCallback.不需要任何其他的代码来使用这方法生效。

使用WaitCallback[c#]的例子

复制代码 代码如下:

void Example() 



    // 连接 ProcessFile 方法到线程池.  

    //注意: 'a' 是一个作为参数的对象  

    ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), a); 



private void ProcessFile(object a) 



    // 我被连接到线程池通过 WaitCallback.  

参数

我们能通过定义一个特定的类并把一些重要的值放在类里面来使用参数,那么,方法接收了对象,就能通过对象向方法传递多个参数了。以下是一个早期的例子。

使用带参数QueueUserWorkItem 的例子[c#]

复制代码 代码如下:

//指定作为线程池方法的参数的类  

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是最小值和最大值中间的位置,以下代码用来初始化进度条:

设置进度条的例子 [C#]

复制代码 代码如下:

//设置进度条的长度.  

// 这里我们有6个单位来完成,所以6是最大值。  

// 最小值通常是0  

progressBar1.Maximum = 6; // 或其他数字  

progressBar1.Minimum = 0; 

进度条位置 你的进度条中的有颜色部分是当前值与最大值的百分比。所以,如果最大值是6,而值是3即表示做完了一半。

ProgressBar 例子 (Windows Forms)

在进度条中调用Invoke(援引)

让我们看如何在进度条实例中使用Invoke方法。遗憾的是,你不能在辅助线程中访问Windows控件,因为UI线程是分离的,必须使用委托(delegate)和Invoke到进度条。

请求Invoke(调用)的例子[C#]

复制代码 代码如下:

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#语言中使用一个锁来避免造成这个字段读和写的错误,锁保护你的线程被其他线程所改变。

计数线程的例子 [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个时它才会工作。这对于一个四核机器是好的。请看描述锁声明的更多上下文的文章

Lock Statement

控制线程计数器

你可以在ThreadPool上使用SetMinThreads 来在连发活动中提高吞吐量和性能。以下是关于可使用的最佳的最小线程数量的材料。

ThreadPool.SetMinThreads Method

总结

我们了解了如何在C#程序中使用线程池来有效管理多个线程,在Windows 窗体应用程序的进度条和用户界面中能给人留很深印象并且也不难实现。然而,线程带来了很多的复杂性并导致漏洞,线程池是一个有用的简化,但它仍然是困难的。

[!--infotagslink--]

相关文章

  • 基于springcloud异步线程池、高并发请求feign的解决方案

    这篇文章主要介绍了基于springcloud异步线程池、高并发请求feign的解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-25
  • C#实现简单的登录界面

    我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • C#中new的几种用法详解

    本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#开发Windows窗体应用程序的简单操作步骤

    这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • C#和JavaScript实现交互的方法

    最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • 轻松学习C#的基础入门

    轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • C#绘制曲线图的方法

    这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • c#自带缓存使用方法 c#移除清理缓存

    这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
  • c#中(&&,||)与(&,|)的区别详解

    这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25