多线程编程之计算限制型异步操作(续)
1. CLR线程池简介
1.1 CLR为什么支持线程池
1.2 线程池ThreadPool管理线程
2. 线程执行上下文
2.1 线程执行上下文简介
2.2 一个简单示例
3. 线程池常见应用情景示例
3.1 将一个线程添加至线程池中(向线程传递参数)
3.2 协作式取消
4. Task对象
5. Task常见编程情景
5.1 创建Task,并启动该Task
5.2 获取Task任务的结果
5.3 Task任务异常处理
5.4 取消Task
5.5 启动新Task
6. 定时器Timer
<4>. Task对象
通过上一篇的介绍,我们知道通过ThreadPool的QueueUserWorkItem方法能够很简单的将一个工作线程添加到线程池中,但是这其中的最大的问题是无法得到工作线程的返回值,还好在.net 4.0中提供了System.Threading.Tasks命名空间中提供了Task类和泛型Task<TResult>类型,非泛型的Task类型(无法得到返回值)如下:
{
// 构造函数重载
public Task(Action action); // 无参
public Task(Action<object> action, object state); // 向action中传递state参数
public Task(Action action, CancellationToken cancellationToken); // 支持协作式取消
// 使用creationOptions标志创建Task,TaskCreationOptions将在下面介绍
public Task(Action action, TaskCreationOptions creationOptions);
// 其他组合构造函数重载版本...
// get属性:AsyncState ,CreationOptions ,CurrentId,Exception,Factory等
// 该任务完成之后,启动另外的任务方法:
public Task ContinueWith(Action<Task> continuationAction);
// 重载版本
public void Start(); // 调用Task的Start来调度任务运行
public void Start(TaskScheduler scheduler); // 使用某个调度器
// 在Task上调用Wait方法将“等待”指定Task结束
public void Wait(); // t.Wait(),将等待t结束
public static void WaitAll(params Task[] tasks); // 等待tasks全部结束
public static int WaitAny(params Task[] tasks);
// ......
}
泛型的Task<TResult>定义除了上面的方法之外,需要特别注意的是:
{
// 构造函数
public Task(Func<TResult> function);
// 得到Task运算结果
public TResult Result { get; internal set; }
}
最后需要特别指出的是 TaskCreationOptions类型,该类型定义在System.Threading.Tasks命名空间中:
{
None = 0,
PreferFairness = 1,
LongRunning = 2, // 需要长时间运行
AttachedToParent = 4, // 附加到父Task中
}
<5>. Task使用情景
5.1 创建Task,并启动该Task
通过前面对Task类型的分析可以看出,Task的构造函数和ThreadPoll的QueueUserWorkItem函数的参数很类似,下面是一个生成并启动Task的代码段:
t.Start();
5.2 获取Task任务的结果
Task相对于线程池来说最重要的一个特性是能够得到Task的返回值,下面演示了这一特性:
5.3 Task任务异常处理
上面的SumInt32函数如果m的值过大的话,那么sum可能出现溢出的情况,于是SumInt32将抛出异常,那么该Exception该如何处理,Task线程池的做法是允许该Task(SumInt32)返回到线程池中,但是在调用t.Result时将抛出System.AggregateException异常。System.AggregateException类型定义如下:
{
// 构造函数
// 通过函数名称Flatten(压平)也能大致了解该函数的功能,英文的解释:If any inner exceptions are themselves instances of AggregateException, // this method will recursively flatten all of them.
public AggregateException Flatten();
// 通过handle函数的predicate函数决定哪些exception是已经处理的,哪些是未处理的。Each invocation of the predicate returns true or false to in // dicate whether the Exception was handled.
public void Handle(Func<Exception, bool> predicate);
// 其他函数
那么一个极端的情况是如果一直不去查询Result属性会怎样?CLR在进行垃圾回收该Task对象时,如果存在未处理的异常,CLR将终止该线程。
5.4 取消Task
}
5.5 启动新Task
上面的代码中为了得到一个Task的返回值的话,那么需要调用Task的Wait方法,这显然降低了性能。一种更好的形式是但Task结束之后,它自动启动另外的Task去处理这个结果。则可以通过Task类的ContinueWith方法实现,该方法定义如下:
public Task ContinueWith(Action<Task<TResult>> continuationAction);
public Task ContinueWith(Action<Task<TResult>> continuationAction, TaskContinuationOptions continuationOptions);
// 其他重载定义
在上面的最后一个重载版本中continuationOptions类型为TaskContinuationOptions,定义如下:
{
None = 0,
// ....
AttachedToParent = 4, // 将Task关联到它的父Task中,这将表明除非所有的子任务完成,否则不会认为父任务完成
// 下面的标识指出在什么情况下运行ContinueWith任务
NotOnRanToCompletion = 65536,
NotOnFaulted = 131072,
OnlyOnCanceled = 196608,
// 更为常用的类型
OnlyOnFaulted = 327680,
OnlyOnRanToCompletion = 393216,
// 其他类型
}
下面是一段示例代码:
Console.ReadKey();
<6>. 定时器Timer
在System.Threading命名空间中存在Timer类,能够实现让线程池在每隔一段时间调用一个方法,Timer定义如下:
{
public Timer(TimerCallback callback);
// 回调方法callback,state向callback传递的参数,dueTime多长时间之后首次调用callback方法,period每次调用callback的时间间隔
public Timer(TimerCallback callback, object state, int dueTime, int period);
public Timer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period);
// 其他重载版本
// 修改dueTime和period参数
public bool Change(int dueTime, int period);
// 其他重载版本
}
一个简单试用示例:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?