Task.Run
和使用 Task
构造函数创建任务是两种不同的方法,它们在某些方面有显著的区别:
-
启动方式:
Task.Run
是一个静态方法,它立即启动一个任务并在后台执行指定的工作。它通常用于快速启动一个简单的后台任务。- 使用
Task
构造函数创建任务,你需要手动启动任务,通常是通过调用.Start()
方法或使用await
关键字。
-
任务启动的线程:
Task.Run
总是使用TaskScheduler
的默认值来启动任务,这通常是ThreadPool
,它负责在线程池的线程上执行任务。- 当使用
Task
构造函数时,你可以指定一个TaskScheduler
,如果你不指定,它也会使用默认的TaskScheduler
。
-
任务的状态管理:
Task.Run
会立即启动任务,并且不提供对任务状态的直接控制。- 使用
Task
构造函数创建的任务,你可以在调用.Start()
之前设置任务的各种属性,例如CancellationToken
。
-
任务的返回值:
Task.Run
可以用于没有返回值的Action
委托,也可以用于有返回值的Func<TResult>
委托。- 使用
Task<TResult>
构造函数创建的任务,你需要指定返回值的类型,并且可以通过.Result
属性或await
获取返回值。
-
任务的取消:
Task.Run
允许你传递一个CancellationToken
来支持任务的取消。- 使用
Task
构造函数创建的任务,同样可以传递CancellationToken
,并在任务执行过程中检查取消请求。
-
任务的异常处理:
Task.Run
启动的任务如果在执行过程中抛出异常,这个异常会被封装在AggregateException
中,并且可以通过Task
对象的Exception
属性访问。- 使用
Task
构造函数创建的任务,异常处理方式与Task.Run
相同。
-
使用场景:
Task.Run
适合快速启动简单的后台任务,特别是当你不需要对任务进行复杂的配置或管理时。- 使用
Task
构造函数创建的任务,适合需要更多控制的场景,例如需要设置特定的TaskScheduler
,或者需要在任务启动之前进行一些准备工作。
// 使用 Task 构造函数创建任务
var task = new Task(() => SomeMethodThatDoesWork());
task.Start(); // 手动启动任务
// 或者使用异步 Lambda 表达式
var taskWithResult = new Task<int>(() => SomeMethodThatReturnsInt());
taskWithResult.Start();
int result = taskWithResult.Result; // 注意:这将阻塞当前线程
如果你需要快速启动一个简单的后台任务,Task.Run
是一个很好的选择。如果你需要更多的控制,例如设置 CancellationToken
或自定义 TaskScheduler
,那么使用 Task
构造函数可能更合适。