C#线程池用法详细介绍

介绍

.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 窗体应用程序的进度条和用户界面中能给人留很深印象并且也不难实现。然而,线程带来了很多的复杂性并导致漏洞,线程池是一个有用的简化,但它仍然是困难的。

(0)

相关推荐

  • 解析C#多线程编程中异步多线程的实现及线程池的使用

    0.线程的本质 线程不是一个计算机硬件的功能,而是操作系统提供的一种逻辑功能,线程本质上是进程中一段并发运行的代码,所以线程需要操作系统投入CPU资源来运行和调度. 1.多线程: 使用多个处理句柄同时对多个任务进行控制处理的一种技术.据博主的理解,多线程就是该应用的主线程任命其他多个线程去协助它完成需要的功能,并且主线程和协助线程是完全独立进行的.不知道这样说好不好理解,后面慢慢在使用中会有更加详细的讲解. 2.多线程的使用: (1)最简单.最原始的使用方法:Thread oGetArgThre

  • C#实现控制线程池最大数并发线程

    1. 实验目的: 使用线程池的时候,有时候需要考虑服务器的最大线程数目和程序最快执行所有业务逻辑的取舍. 并非逻辑线程越多也好,而且新的逻辑线程必须会在线程池的等待队列中等待 ,直到线程池中工作的线程执行完毕, 才会有系统线程取出等待队列中的逻辑线程,进行CPU运算. 2.  解决问题: <a>如果不考虑服务器实际可支持的最大并行线程个数,程序不停往线程池申请新的逻辑线程,这个时候我们可以发现CPU的使用率会不断飙升,并且内存.网络带宽占用也会随着逻辑线程在CPU队列中堆积,而不断增大. &l

  • C#线程池操作方法

    本文实例讲述了C#线程池操作方法.分享给大家供大家参考.具体如下: static void Main(string[] args) { //设置线程池中的线程数最大为1000, //第一个为工作者线程,第二个为I/O线程 ThreadPool.SetMaxThreads(1000, 1000); for (int i = 0; i < 10;i ) { ThreadPool.QueueUserWorkItem(new WaitCallback(ShowMessage), string.Forma

  • C#线程处理系列之线程池中的I/O线程

    一.I/O线程实现对文件的异步  1.1  I/O线程介绍: 对于线程所执行的任务来说,可以把线程分为两种类型:工作者线程和I/O线程. 工作者线程用来完成一些计算的任务,在任务执行的过程中,需要CPU不间断地处理,所以,在工作者线程的执行过程中,CPU和线程的资源是充分利用的. I/O线程主要用来完成输入和输出的工作的,在这种情况下, 计算机需要I/O设备完成输入和输出的任务,在处理过程中,CPU是不需要参与处理过程的,此时正在运行的线程将处于等待状态,只有等任务完成后才会有事可做, 这样就造

  • C#实现线程池的简单示例

    本文以实例演示了C#线程池的简单实现方法.程序中定义了一个对象类,用以包装参数,实现多个参数的传递.成员属性包括两个输入参数和一个输出参数.代码简单易懂,备有注释便于理解. 具体实现代码如下: using System; using System.Threading; //定义对象类,用以包装参数,实现多个参数的传递 class Packet { //成员属性包括两个输入参数和一个输出参数 protected internal String inval1; protected internal

  • C#多线程学习之(四)使用线程池进行多线程的自动管理

    本文实例讲述了C#多线程学习之使用线程池进行多线程的自动管理.分享给大家供大家参考.具体如下: 在多线程的程序中,经常会出现两种情况: 一种情况:   应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应 这一般使用ThreadPool(线程池)来解决: 另一种情况:线程平时都处于休眠状态,只是周期性地被唤醒 这一般使用Timer(定时器)来解决: ThreadPool类提供一个由系统维护的线程池(可以看作一个线程的容器),该容器需要 Windows 2000 以上系

  • C#线程池用法详细介绍

    介绍 .NET Framework提供了包含ThreadPool类的System.Threading 空间,这是一个可直接访问的静态类,该类对线程池是必不可少的.它是公共"线程池"设计样式的实现.对于后台运行许多各不相同的任务是有用的.对于单个的后台线种而言有更好的选项. 线程的最大数量.这是完全无须知道的.在.NET中ThreadPool的所有要点是它自己在内部管理线程池中线程.多核机器将比以往的机器有更多的线程.微软如此陈述"线程池通常有一个线程的最大数量,如果所有的线程

  • Java四种常用线程池的详细介绍

    一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池.使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务. 2. 线程池的工作机制 2.1 在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程

  • 浅谈Spring @Async异步线程池用法总结

    本文介绍了Spring @Async异步线程池用法总结,分享给大家,希望对大家有帮助 1. TaskExecutor spring异步线程池的接口类,其实质是Java.util.concurrent.Executor Spring 已经实现的异常线程池: 1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程. 2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作.只适用于不需要多线程的地方 3. Conc

  • Java多线程的用法详细介绍

    Java多线程的用法详细介绍 最全面的Java多线程用法解析,如果你对Java的多线程机制并没有深入的研究,那么本文可以帮助你更透彻地理解Java多线程的原理以及使用方法. 1.创建线程 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例.Thread构造函数: public Thread( ); publi

  • 非常适合新手学生的Java线程池超详细分析

    目录 线程池的好处 创建线程池的五种方式 缓存线程池CachedThreadPool 固定容量线程池FixedThreadPool 单个线程池SingleThreadExecutor 定时任务线程池ScheduledThreadPool ThreadPoolExecutor创建线程池(十分推荐) ThreadPoolExecutor的七个参数详解 workQueue handler 如何触发拒绝策略和线程池扩容? 线程池的好处 可以实现线程的复用,避免重新创建线程和销毁线程.创建线程和销毁线程对

  • Java InheritableThreadLocal用法详细介绍

    目录 简介 问题复现 解决方案 源码分析 注意 简介 本文介绍InheritableThreadLocal的用法. ThreadLocal可以将数据绑定当前线程,如果希望当前线程的ThreadLocal的数据被子线程使用,实现方式就会相当困难(需要用户自己在代码中传递). InheritableThreadLocal可以方便地让子线程自动获取父线程ThreadLocal的数据. ThreadLocal和InheritableThreadLocal都要注意,用完后要调用其remove()方法,不然

  • nginx命令参数用法详细介绍

    nginx命令参数用法详细介绍 nginx命令:启动nginx 在Windows上安装好nginx后,我们需要启动nginx服务,启动nginx服务的命令行操作主要有两种方式,即 C:/nginx-0.8.53>nginx.exe 或者 C:/nginx-0.8.53>start nginx 启动nginx命令说明:需要注意,由于nginx默认端口也是80端口,如果此时你的机器上开启了Apache或者IIS服务,切忌在启动nginx之前务必关闭IIS或Apache服务,否则nginx启动命令不

  • PHP中error_reporting函数用法详细介绍

    PHP中error_reporting函数用法详细介绍 PHP中对错误的处理会用到error_reporting函数,看到最多的是error_reporting(E_ALL ^ E_NOTICE),这个是什么意思呢?下面我们具体分析error_reporting函数. 定义用法 error_reporting() 设置 PHP 的报错级别并返回当前级别. 语法 error_reporting(report_level) 如果参数 report_level 未指定,当前报错级别将被返回.下面几项是

  • Spring中@Transactional用法详细介绍

    Spring中@Transactional用法详细介绍 引言: 在spring中@Transactional提供一种控制事务管理的快捷手段,但是很多人都只是@Transactional简单使用,并未深入了解,其各个配置项的使用方法,本文将深入讲解各个配置项的使用. 1.  @Transactional的定义 Spring中的@Transactional基于动态代理的机制,提供了一种透明的事务管理机制,方便快捷解决在开发中碰到的问题.在现实中,实际的问题往往比我们预期的要复杂很多,这就要求对@Tr

  • 四种Java线程池用法解析

    本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } } ).start(); 那你就out太多了,new Thread的弊端如下: a. 每次new Thread新建对象性能差. b. 线程缺乏统一管理,可能无限

随机推荐