多线程3:使用线程池

1、线程池
线程池可以适应于需要大量短暂的开销大的资源的情形。
线程池会事先在资源池中分配一定的线程资源。需要新的资源时,从池中取出一个,而不用创建新的资源。当该资源不再使用时,就将其返回到池中。
线程池是受CLR管理的,即每个CLR都有一个线程池实例。
ThreadPool类有一个QueueUserWorkItem静态方法,它接受一个委托,代表用户自定义的一个异步操作。
该方法被调用后,委托会进入内部队列中。
如果线程池中没有任何线程,将创建一个新的工作者线程并将队列中第一个委托放入到该工作者线程中。
要保持线程池中的操作都是短暂的。
线程池中放入长时间运行的操作,或者阻塞工作者线程,会导致所有工作者线程变得繁忙,从而无法服务用户操作。
这会导致性能问题和非常难以调试的错误。
当停止向线程池中放置新操作时,线程池最终会删除一定时间后过期的不再使用的线程,以释放不再需要的系统资源。
在ASP.NET中只推荐使用输入/输出密集型的异步操作,因为其使用了一个不同的方式,叫做I/O线程。
线程池中的工作者线程都是后台线程。

2、异步编程模型(APM)
Asynchronous Programming Model,简称APM,这是.NET历史中第一个异步编程模式。
委托的BeginInvoke方法接受一个回调函数及一个用户自定义状态。
异步操作完成后回调函数会被调用,用户自定义状态会传给该回调函数,该状态通常用于区分异步调用。
BeginInvoke会立即返回IAsyncResult类型的对象,当线程池中的工作者线程在执行异步操作时,仍允许我们进行其他工作。
使用IAsyncResult的IsCompleted属性可以获取任务是否已经完成,也可以使用AsyncWaitHandle属性等待操作完成。
异步操作完成后,通过委托调用EndInvoke方法,传递委托参数和IAsyncResult对象。
调用EndInvoke方法本身会等待异步操作完成,该方法会将任何未处理的异常抛回到调用线程。
使用异步编程模型(APM)时,应该始终确保调用了Begin和End方法。
传递给BeginInvoke方法的回调函数会被放到线程池中的一个工作者线程中。
使用BeginOperationName/EndOperationName方法和.NET中的IAsyncResult对象等方式被称为异步编程模型(或APM模式),这样的方法对称为异步方法。
在现代编程中,更推荐使用任务并行库(Task Parallel Library,简称TPL)来组织异步API。
需要注意的是,.Net5不支持BeginInvoke,即不支持APM模型。

3、使用ThreadPool.QueueUserWorkItem方法
使用ThreadPool.QueueUserWorkItem方法可以向线程池中添加异步操作。
ThreadPool.QueueUserWorkItem方法第一个参数是一个含有object类型参数的无返回值委托对象。
ThreadPool.QueueUserWorkItem方法可选第二个object类型参数作为上述委托的参数传入执行的异步操作函数中。
使用lambda表达式作为ThreadPool.QueueUserWorkItem方法的参数简洁有效,可使用闭包机制向其传入参数。

4、线程池的并行度
.Net5中线程池默认最小线程个数为16个,最大为32767。

5、取消异步操作
CancellationTokenSource和CancellationToken类是实现异步操作取消的事实标准,在.Net4.0被引入。
有3种方法可以实现异步操作取消:
(1) 轮询CancellationToken.IsCancellationRequested属性,为true则需要取消当前异步操作。
(2) 抛出OperationCancelledException异常。
(3) 注册回调函数。当操作被取消时,线程池将会调用该回调函数。这允许链式传递一个取消逻辑到另一个异步操作中。

6、在线程池中等待及处理超时
ThreadPool.RegisterWaitForSingleObject方法可以实现线程池中的操作超时处理。
该方法允许在线程池中的队列中放入一个回调函数,当提供的等待事件处理器收到信号或者超时时,该回调函数将被调用。
当等待事件处理器超时时,可以使用CancellationToken取消等待的异步操作,以实现超时的相关逻辑处理。
该方式适合于有大量线程必须处于阻塞状态中,等待一些多线程事件发信号的情况。
借助于线程池的基础设施,无需阻塞所有这样的线程,可以将其释放直到信号事件被设置。

7、计时器System.Threading.Timer
System.Threading.Timer可以实现计时器功能。
System.Threading.Timer的第一个参数是定时器回调,第二个参数表示用户状态,
第三个参数表示多久后第一次运行回调,第四个参数表示运行定时器的间隔时间。
该计时器类基于线程池,当时间满足条件后,会从从线程池中取出一个工作者线程,调用计时器类设置的回调方法。
可以通过设置Timeout.Infinite值给计时器一个时间间隔,表示只允许一次操作,
然后在回调函数中根据业务需要设置下一次计时器将被操作的时间。

8、BackgroundWorker
BackgroundWorker的事件处理器基于线程池,其事件通知机制会在线程池工作线程中进行处理。
BackgroundWorker的WorkerReportsProgress属性指示是否支持进度通知。
BackgroundWorker的WorkerSupportsCancellation属性指示是否支持取消操作。
BackgroundWorker组件完成时可以获取操作结果:完成、发生错误还是取消。
BackgroundWorker组件是基于事件的异步模式,即Event-based Asynchronous Pattern,简称EAP。
EAP是.NET历史上第二种用来构造异步程序的方式,现在更推荐使用TPL。

posted @   xhubobo  阅读(110)  评论(0编辑  收藏  举报
编辑推荐:
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
点击右上角即可分享
微信分享提示