C#中Task介绍
在C#中,Task类是异步编程的重要工具之一,它提供了一系列用于创建、启动和管理异步任务的常用方法,主要包括以下几个:
- Task.Run:创建并开始执行一个异步任务。
Task task = Task.Run(() =>
{
// 执行一些耗时操作
});
- Task.Factory.StartNew:使用TaskFactory类来创建并启动一个异步任务,并可以配置任务的类型和选项等。
Task task = Task.Factory.StartNew(() =>
{
// 执行一些耗时操作
}, TaskCreationOptions.LongRunning);
- Task.FromResult:将一个已完成的任务包装成一个新的Task对象返回。
Task<int> completedTask = Task.FromResult(42);
- Task.Delay:创建并返回一个暂停指定时间后完成的异步任务。
await Task.Delay(TimeSpan.FromSeconds(5));
- Task.WaitAny:等待任意一个异步任务完成后继续执行。它可以阻塞执行当前代码的线程,不建议在UI线程中使用。
Task<int>[] tasks = new Task<int>[3];
tasks[0] = Task.Run(() => DoWork());
tasks[1] = Task.Run(() => DoMoreWork());
tasks[2] = Task.Run(() => DoOtherWork());
int index = await Task.WaitAny(tasks);
int result = tasks[index].Result;
- Task.WaitAll:等待所有异步任务完成后继续执行。它同样可以阻塞线程运行,不建议在UI线程中使用。
Task<int>[] tasks = new Task<int>[3];
tasks[0] = Task.Run(() => DoWork());
tasks[1] = Task.Run(() => DoMoreWork());
tasks[2] = Task.Run(() => DoOtherWork());
await Task.WaitAll(tasks);
int result1 = await tasks[0];
int result2 = await tasks[1];
int result3 = await tasks[2];
- Task.ContinueWith:使用新的任务来代表原有任务的完成情况,支持链式调用。在原有任务完成后,可以根据需要添加一些后续操作。
Task<int> task1 = Task.Run(() => DoWork());
Task<string> task2 = task1.ContinueWith(t => FormatResult(t.Result));
需要注意的是,在使用Task时,应该避免出现async void方法、潜在死锁、延迟造成的线程启动等问题,以及适时地将一些阻塞操作转换为异步操作,从而获得更高效的异步编程体验。
Task.Run和Task.Factory.StartNew
C#中的Task.Run和Task.Factory.StartNew方法都可以用于创建并启动一个异步任务,它们本质上都是将一个委托或Lambda表达式作为方法参数来实现异步执行。然而,它们之间确实存在一些差异,包括以下几个方面:
-
Task.Factory.StartNew方法允许使用TaskCreationOptions枚举类型的选项来配置任务的行为和特性,比如指定是否应该为长时间运行的任务分配单独的线程,或者是否应该禁用任务延迟等。而Task.Run方法则没有提供这样的选项,会使用默认设置。
-
Task.Factory.StartNew方法返回一个Task,而Task.Run方法直接返回一个已启动的Task。由于这种区别,使用Task.Run方法时更加简便和直接,不需要再使用StartNew方法的Task.Run(delegate)格式写代码。
-
目前,推荐使用Task.Run方法而不是Task.Factory.StartNew方法。这是由于在.NET Framework 4.5中对Task.Run进行了优化,使其更容易使用、更可读、更安全、更高效,并避免了一些常见但难以处理的问题,比如任务调度延迟和死锁等。
综上所述,Task.Run方法比Task.Factory.StartNew方法更常用和更受推荐,既可读性更好,又能够实现大多数异步操作需求。但是,在某些特定场合下,使用Task.Factory.StartNew能够提供更精细的线程投放控制能力和一些必要特性。
需要注意的是,在使用Task.Run或Task.Factory.StartNew时,开启的线程数不应该过多,要适当评估运行环境的CPU和I/O资源,并选择正确的选项进行任务调度,避免出现不必要的资源浪费、线程饥饿和队列拥堵等