C#线程学习笔记二:线程池中的工作者线程
本笔记摘抄自:https://www.cnblogs.com/zhili/archive/2012/07/18/ThreadPool.html,记录一下学习过程以备后续查用。
一、线程池基础
首先,创建和销毁线程是一个要耗费大量时间的过程,其次,太多的线程也会浪费内存资源,所以通过Thread类来创建过多的线程反而有损于性能。为了改善这样
的问题 ,.NET中就引入了线程池。
线程池形象的表示就是存放应用程序中使用的线程的一个集合(就是放线程的地方,这样线程都放在一个地方就好管理了)。
CLR初始化时,线程池中是没有线程的,在内部, 线程池维护了一个操作请求队列。当应用程序想执行一个异步操作时,就调用一个方法,将一个任务放到线程池
的队列中,线程池代码从队列中提取任务,将这个任务委派给一个线程池线程去执行,当线程池线程完成任务时,线程不会被销毁,而是返回到线程池中,等待响应另
一个请求。由于线程不被销毁, 这样就可以避免因为创建线程所产生的性能损失。
MSDN表述:
“线程池经常用在服务器应用程序中,每一个新进来的需求被分配给一个线程池中的线程,这样该需求能被异步的执行,没有阻碍主线程或推迟后继需求的处理。”
注意:通过线程池创建的线程默认为后台线程,优先级默认为Normal。
二、通过线程池的工作者线程实现异步
2.1创建工作者线程的方法
public static bool QueueUserWorkItem (WaitCallback callback);
public static bool QueueUserWorkItem(WaitCallback callback, Object state);
这两个方法向线程池的队列添加一个工作项(work item)以及一个可选的状态数据,然后,这两个方法就会立即返回。
工作项其实就是由callback参数标识的一个方法,该方法将由线程池线程执行。同时写的回调方法必须匹配System.Threading.WaitCallback委托类型,定义为:
public delegate void WaitCallback(Object state);
下面演示如何通过线程池线程来实现异步调用:
class Program
{
static void Main(string[] args)
{
#region 通过线程池的工作者线程实现异步
//设置线程池中工作者线程最大数量为1000,I/O线程最大数量为1000。
ThreadPool.SetMaxThreads(, );
Console.WriteLine("Main thread: queue an asynchronous method.");
PrintMessage("Main thread start."); //把工作项添加到队列中,此时线程池会用工作者线程去执行回调方法。
ThreadPool.QueueUserWorkItem(AsyncMethod);
Console.Read();
#endregion
} /// <summary>
/// 打印线程池信息
/// </summary>
/// <param name="data"></param>
private static void PrintMessage(string data)
{
//获得线程池中可用的工作者线程数量及I/O线程数量
ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber); Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
data,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsBackground.ToString(),
workThreadNumber.ToString(),
ioThreadNumber.ToString());
} /// <summary>
/// 异步方法:必须匹配WaitCallback委托
/// </summary>
/// <param name="state"></param>
private static void AsyncMethod(object state)
{
Thread.Sleep();
PrintMessage("Asynchoronous method.");
Console.WriteLine("Asynchoronous thread has worked.");
}
}
运行结果如下:

从结果中可以看出,线程池中的可用的工作者线程少了一个,用去执行回调方法了。
ThreadPool.QueueUserWorkItem(WaitCallback callback,Object state) 方法可以把object对象作为参数传送到回调函数中,使用方法与
ThreadPool.QueueUserWorkItem(WaitCallback callback)类似,这里就不列出了。
2.2 协作式取消
.NET Framework提供了取消操作的模式, 这个模式是协作式的。为了取消一个操作,首先必须创建一个System.Threading.CancellationTokenSource对象。
下面代码演示协作式取消的使用,主要实现当用户在控制台敲下回车键后就停止数数方法。
class Program
{
static void Main(string[] args)
{
#region 协作式取消
ThreadPool.SetMaxThreads(, );
Console.WriteLine("Main thread run.");
PrintMessage("Start");
Run();
Console.ReadKey();
#endregion
} /// <summary>
/// 打印线程池信息
/// </summary>
/// <param name="data"></param>
private static void PrintMessage(string data)
{
//获得线程池中可用的工作者线程数量及I/O线程数量
ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber); Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
data,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsBackground.ToString(),
workThreadNumber.ToString(),
ioThreadNumber.ToString());
} /// <summary>
/// 运行工作者线程(包含协作式取消)
/// </summary>
private static void Run()
{
CancellationTokenSource cts = new CancellationTokenSource(); //这里是用Lambda表达式的写法,效果一样。
//ThreadPool.QueueUserWorkItem(obj => Count(cts.Token, 1000)); ThreadPool.QueueUserWorkItem(Callback, cts.Token);
Console.WriteLine("Press enter key to cancel the operation.\n");
Console.ReadLine();
//传达取消请求
cts.Cancel();
} /// <summary>
/// 回调函数
/// </summary>
/// <param name="state"></param>
private static void Callback(object state)
{
Thread.Sleep();
PrintMessage("Asynchoronous method start.");
CancellationToken token = (CancellationToken)state;
Count(token, );
} /// <summary>
/// 数数
/// </summary>
/// <param name="token"></param>
/// <param name="countTo"></param>
private static void Count(CancellationToken token, int countTo)
{
for (int i = 1; i <= countTo; i++)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("Count is canceled.");
break;
} Console.WriteLine(i);
Thread.Sleep();
}
Console.WriteLine("Count has done.");
}
}
运行结果如下:

三、使用委托实现异步
涉及术语解释--异步编程模型:
APM 异步编程模型(Asynchronous Programming Model)
EAP 基于事件的异步编程模式(Event-based Asynchronous Pattern)
TAP 基于任务的异步编程模式(Task-based Asynchronous Pattern)
通过调用ThreadPool的QueueUserWorkItem方法来来启动工作者线程非常方便,但委托WaitCallback指向的是带有一个参数的无返回值的方法。如果我们实际操作中
需要有返回值,或者需要带有多个参数, 这时通过这样的方式就难以实现了。 为了解决这样的问题,我们可以通过委托来建立工作这线程。
下面代码演示使用委托实现异步:
class Program
{
//使用委托实现异步,是使用了异步编程模型APM。
private delegate string ThreadDelegate(); static void Main(string[] args)
{
#region 使用委托实现异步
ThreadPool.SetMaxThreads(, );
PrintMessage("Main thread start."); //实例化委托
ThreadDelegate threadDelegate = new ThreadDelegate(AsyncMethod);
//异步调用委托
IAsyncResult result = threadDelegate.BeginInvoke(null, null);
//获取结果并打印
string returnData = threadDelegate.EndInvoke(result);
Console.WriteLine(returnData);
Console.ReadLine();
#endregion
} /// <summary>
/// 打印线程池信息
/// </summary>
/// <param name="data"></param>
private static void PrintMessage(string data)
{
//获得线程池中可用的工作者线程数量及I/O线程数量
ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber); Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
data,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsBackground.ToString(),
workThreadNumber.ToString(),
ioThreadNumber.ToString());
} /// <summary>
/// 异步方法
/// </summary>
/// <returns></returns>
private static string AsyncMethod()
{
Thread.Sleep();
PrintMessage("Asynchoronous method.");
return "Method has completed.";
}
}
运行结果如下:

四、任务
同样,任务的引入也是为了解决通过ThreadPool.QueueUserWorkItem中限制的问题。
4.1 使用任务来实现异步
class Program
{
static void Main(string[] args)
{
#region 使用任务实现异步
ThreadPool.SetMaxThreads(, );
PrintMessage("Main thread start.");
//调用构造函数创建Task对象
Task<int> task = new Task<int>(n => AsyncMethod((int)n), ); //启动任务
task.Start();
//等待任务完成
task.Wait();
Console.WriteLine("The method result is: " + task.Result);
Console.ReadLine();
#endregion
} /// <summary>
/// 打印线程池信息
/// </summary>
/// <param name="data"></param>
private static void PrintMessage(string data)
{
//获得线程池中可用的工作者线程数量及I/O线程数量
ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber); Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
data,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsBackground.ToString(),
workThreadNumber.ToString(),
ioThreadNumber.ToString());
} /// <summary>
/// 异步方法
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
private static int AsyncMethod(int n)
{
Thread.Sleep();
PrintMessage("Asynchoronous method."); int sum = ;
for (int i = ; i < n; i++)
{
//运算溢出检查
checked
{
sum += i;
}
} return sum;
}
}
运行结果如下:

4.2 取消任务
如果要取消任务, 同样也可以CancellationTokenSource对象来取消。
下面代码演示取消一个任务:
class Program
{
static void Main(string[] args)
{
#region 取消任务
ThreadPool.SetMaxThreads(, );
PrintMessage("Main thread start.");
CancellationTokenSource cts = new CancellationTokenSource(); //调用构造函数创建Task对象,将一个CancellationToken传给Task构造器从而使Task和CancellationToken关联起来。
Task<int> task = new Task<int>(n => AsyncMethod(cts.Token, (int)n), ); //启动任务
task.Start();
//延迟取消任务
Thread.Sleep(); //取消任务
cts.Cancel();
Console.WriteLine("The method result is: " + task.Result);
Console.ReadLine();
#endregion
} /// <summary>
/// 打印线程池信息
/// </summary>
/// <param name="data"></param>
private static void PrintMessage(string data)
{
//获得线程池中可用的工作者线程数量及I/O线程数量
ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber); Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
data,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsBackground.ToString(),
workThreadNumber.ToString(),
ioThreadNumber.ToString());
} /// <summary>
/// 异步方法
/// </summary>
/// <param name="ct"></param>
/// <param name="n"></param>
/// <returns></returns>
private static int AsyncMethod(CancellationToken ct, int n)
{
Thread.Sleep();
PrintMessage("Asynchoronous method."); int sum = ;
try
{
for (int i = ; i < n; i++)
{
//当CancellationTokenSource对象调用Cancel方法时,就会引起OperationCanceledException异常,
//通过调用CancellationToken的ThrowIfCancellationRequested方法来定时检查操作是否已经取消,
//这个方法和CancellationToken的IsCancellationRequested属性类似。
ct.ThrowIfCancellationRequested();
Thread.Sleep();
//运算溢出检查
checked
{
sum += i;
}
}
}
catch (Exception e)
{
Console.WriteLine("Exception is:" + e.GetType().Name);
Console.WriteLine("Operation is canceled.");
} return sum;
}
}
运算结果如下:

4.3 使用任务工厂实现异步操作
同样也可以通过任务工厂TaskFactory类型来实现异步操作。
class Program
{
static void Main(string[] args)
{
#region 使用任务工厂实现异步
ThreadPool.SetMaxThreads(, );
Task.Factory.StartNew(() => PrintMessage("Main thread."));
Console.Read();
#endregion
} /// <summary>
/// 打印线程池信息
/// </summary>
/// <param name="data"></param>
private static void PrintMessage(string data)
{
//获得线程池中可用的工作者线程数量及I/O线程数量
ThreadPool.GetAvailableThreads(out int workThreadNumber, out int ioThreadNumber); Console.WriteLine("{0}\n CurrentThreadId is:{1}\n CurrentThread is background:{2}\n WorkerThreadNumber is:{3}\n IOThreadNumbers is:{4}\n",
data,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsBackground.ToString(),
workThreadNumber.ToString(),
ioThreadNumber.ToString());
}
}
运行结果如下:
C#线程学习笔记二:线程池中的工作者线程的更多相关文章
- Java学习笔记二十:Java中的内部类
Java中的内部类 一:什么是内部类: (1).什么是内部类呢? 内部类( Inner Class )就是定义在另外一个类里面的类.与之对应,包含内部类的类被称为外部类. (2).那为什么要将一个类定 ...
- Java学习笔记二十七:Java中的抽象类
Java中的抽象类 一:Java抽象类: 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就 ...
- CodeIgniter学习笔记二:CI中的query_builder(AR)、连贯操作
一.开启query_builder 在application\config\database.php中添加如下代码(默认已开启): $query_builder = TRUE; 二.查询数据 //ge ...
- 【Intel AF 2.1 学习笔记二】AF中的页面——Panel
Panel Panel控件是你的app中的独立内容的区域控件.它是af UI的核心.Panel div 元素实际上承载了app中你管理和显示的界面元素和内容. 创建panel控件是相当地容易的:在id ...
- MyBatis学习笔记二:MyBatis生产中使用环境搭建
这里是在上一个环境的基础上修改的,这里就不在给出所有的配置,只给出哪里修改的配置 1.修改POJO对象为注解方式 2.创建Dao层接口 package com.orange.dao; import c ...
- Linux进程线程学习笔记:运行新程序
Linux进程线程学习笔记:运行新程序 周银辉 在上一篇中我们说到,当启动一个新进程以后,新进程会复制父进程的大部份上下 ...
- C#线程学习笔记九:async & await入门二
一.异步方法返回类型 只能返回3种类型(void.Task和Task<T>). 1.1.void返回类型:调用方法执行异步方法,但又不需要做进一步的交互. class Program { ...
- 并发编程学习笔记(3)----synchronized关键字以及单例模式与线程安全问题
再说synchronized关键字之前,我们首先先小小的了解一个概念-内置锁. 什么是内置锁? 在java中,每个java对象都可以用作synchronized关键字的锁,这些锁就被称为内置锁,每个对 ...
- 重新想象 Windows 8 Store Apps (42) - 多线程之线程池: 延迟执行, 周期执行, 在线程池中找一个线程去执行指定的方法
[源码下载] 重新想象 Windows 8 Store Apps (42) - 多线程之线程池: 延迟执行, 周期执行, 在线程池中找一个线程去执行指定的方法 作者:webabcd 介绍重新想象 Wi ...
随机推荐
- Jpa支持LocalDateTime类型持久化
package com.boldseas.porscheshop.common.config; import javax.persistence.AttributeConverter; import ...
- 服务容错保护hystrix
灾难性雪崩效应 如何解决灾难性雪崩效应 降级 超时降级.资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据.实现一个 fallback 方法, 当请求后端服务出现异常的时候, 可以使用 ...
- HTML5学习第二天!
学习了一天,然后整理内容到现在,感觉昨天的学习效率有点差,哎! 感受尽在代码中,布局真的脑壳疼,仅仅只整理了CSS中的list: <!DOCTYPE html> <html> ...
- openlayers5-webpack 入门开发系列一初探篇(附源码下载)
前言 openlayers5-webpack 入门开发系列环境知识点了解: node 安装包下载webpack 打包管理工具需要依赖 node 环境,所以 node 安装包必须安装,上面链接是官网下载 ...
- 马拉车manacher
目的:线性查找一个串的最长回文子串 时间复杂度:O(n) len[i]表示以i为中心的回文串的半径,mx即为当前计算回文串最右边字符的最大值,p是中心点mid,mx-i和2*p-1关于p对称 http ...
- 上手spring boot项目(三)之spring boot整合mybatis进行增删改查
使用mybatis框架进行增删改查大致有两种基础方式,一种扩展方式.两种基础方式分别是使用xml映射文件和使用方法注解.扩展方式是使用mybatis-plus的方式,其用法类似于spring-data ...
- 使用Spring Boot和AspectJ实现方法跟踪基础结构
了解如何使用Spring Boot和AspectJ实现方法跟踪基础结构!最近在优锐课学习收获颇多,记录下来大家一起进步! 在我们的应用程序中,获取方法的堆栈跟踪信息可能会节省很多时间.具有输入输出参数 ...
- Java开发数据库设计的14个技巧,你知道几个?
1. 原始单据与实体之间的关系 可以是一对一.一对多.多对多的关系.在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体.在特殊情况下,它们可能是一对多或多对一的关系,即一张原始单证对 ...
- autocad 二次开发 最小包围圆算法
autocad 二次开发 最小包围圆算法 主要实现了在模型空间下的得到一个包围所有图元的最小圆,该算法的思路是这样:1.从点集中随机选出两个点作为直径对圆进行初始化.2.判断下一个点p是否在圆中,如果 ...
- ESD选型
通常情况ESD保护电路如下 当系统没有干扰,正常工作时,ESD器件可以忽略,几乎不起作用 当外部接口电压超过ESD器件的击穿电压(VBR),ESD器件开始起作用,并将电流分流到地. 实际ESD器件的工 ...