type
status
date
slug
summary
tags
category
icon
password
Property
Aug 9, 2024 11:58 AM
Thread的问题
线程(Thread)是用来创建并发(concurrency)的一种低级别工具,它有一些限制,尤其是:
- 虽然开始线程的时候可以方便的传入数据,但是当Join的时候,很难从线程获得返回值。
- 可能需要设置一些共享字段
- 如果操作抛出异常,捕获和传播改异常都很麻烦
- 午饭告诉线程结束时开始做另外的工作,你必须进行Join操作(在进程中阻塞当前的线程)
很难使用较小的并发(concurrency)来组建大型的并发
导致了对手动同步的更大依赖以及随之而来的问题
Task class
Task类可以很好地解决上述问题
Task是一个相对高级的抽象:他代表了一个并发操作(concurrent)
- 该操作可能由Thread支持,或不由Thread支持
Task是可组合的(可使用Continuation把它们串成链)
- Tasks可以使用线程池来减少启动延迟
- 使用TackCompletionSource,Tasks可以利用回调的方式,在等待I/O绑定操作时完全避免线程
开始一个Task Task.Run
Task类在System.Threading.Tasks命名空间下
开始一个Task最简单的办法就是使用Task.Run(.NET 4.5,4.0的时候是Task.Factory.StartNew)这个静态方法
- 传入一个Action委托即可(例子task)
Task默认使用线程池,也就是后台线程
- 当主线程结束时,你创建的是有tasks都会结束(例子task)
Task.Run返回一个Task对象,可以使用它来监视其过程
- 在Task.Run之后,我们没有调用Start,因为该方法创建的是“热”任务(hot task)
- 可以通过Task的构造函数创建“冷”任务(cold task),但是很少这样做
可以通过Task的Status属性来跟踪task的执行状态
什么都不会输出,随着主线程结束就结束了
下面的就会输出
Wait 等待
调用task的Wait方法会进行阻塞直到操作完成
- 相当于调用thread上的Join方法
Wait也可以让你制定一个超时时间和一个取消令牌来提前结束等待
长时间运行的任务 Long-running tasks
默认情况下,CLR在线程池中运行Task,这非常适合短时间运行的Compute-Bound类工作
针对长时间运行的任务或者阻塞操作(例如前面的例子),你可以不采用线程池(例子longRunning)
如果同时运行多个long-running tasks(尤其是其中有处于阻塞状态的),那么性能将会受很大影响,这时有比TaskCreationOptions.LongRunning更好的办法
- 如果任务是IO-Bound,TaskCompletionSource和异步函数可以让你用回调(Coninuation)代替线程实现并发
- 如果任务是Compute-Bound,生产者/消费者队列允许你对任务的并发性进行限流,避免把其他线程和进程饿死
- 作者:Kitety
- 链接:https://www.kitety.com/article/c-sharp-async-create-task
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章