Task
Task基础
Task在线程池的基础上进行了优化,并提供了更多的API。在FCL4.0中,如果我们要编写多线程程序,Task显然已经优于传统的方式。
若要等待单个任务完成,可以调用其 Task.Wait方法。 对方法的调用会 Wait阻止调用线程,直到单类实例执行完毕。
以下是一个简单的任务示例:
class Program {
static void Main(string[] args) {
Task task = new Task((() => {
Thread.Sleep(3000);
}));
task.Start();
task.ContinueWith((task1 => {
Console.WriteLine("任务完成");
Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task1.IsCanceled, task1.IsCompleted, task1.IsFaulted);
}));
Console.ReadKey();
}
}
ContinueWith是任务的串行,当任务完成后会执行ContinueWith中的代码。
Task的创建
Task的创建有很多种方式,主要分为带返回值和不带返回值的方式,这里会详细讲述。
不带返回值的方式
第一种创建方式
class Program {
static void Main(string[] args) {
Task task = new Task((() => {
Thread.Sleep(3000);
Console.WriteLine("Task Run Finish");
}));
task.Start();
task.Wait(); // 或者Task.WaitAll(task);
task.Dispose();
Console.ReadKey();
}
}
其中task.Wait()是等待当前Task执行完毕,而Task.WaitAll(...)可以用于等待所有任务执行完毕。
除此之外,构造函数还提供另外一种创建方式:
public Task (Action<object?> action, object? state);
其中action是要异步执行的操作委托,而state包含由 action
委托使用的数据的对象。
第二种创建方式
class Program {
static void Main(string[] args) {
Task task = Task.Run((() => {
Thread.Sleep(3000);
Console.WriteLine("Task Run Finish");
}));
Console.ReadKey();
}
}
第三种创建方式
class Program {
static void Main(string[] args) {
Task task = Task.Factory.StartNew((() => {
Thread.Sleep(3000);
Console.WriteLine("Task Run Finish");
}));
Console.ReadKey();
}
}
还可以将任务标记为长时间运行任务,此时任务不会使用线程池,而是在单独线程中运行。
class Program {
static void Main(string[] args) {
Task task = Task.Factory.StartNew(() => Thread.Sleep(3000), TaskCreationOptions.LongRunning);
Console.ReadKey();
}
}
在上述代码中,创建Task的格式如下所示:
public System.Threading.Tasks.Task StartNew (Action<object?> action, object? state);
其中action是要异步执行的操作委托,而state包含由 action
委托使用的数据的对象。
第四种创建方式
创建Task还可以使用async/await的方式来实现。
class Program {
static void Main(string[] args) {
AsyncFunction();
Console.ReadKey();
}
async static void AsyncFunction() {
await Task.Delay(3000);
Console.WriteLine("Task Run Finish");
}
}
带返回值的方式
第一种创建方式
class Program {
static void Main(string[] args) {
Task<int> task = new Task<int>((() => {
Thread.Sleep(3000);
Console.WriteLine("Task Run Finish");
return 0;
}));
task.Start();
int resulr = task.Result;
Console.WriteLine("Task Result:" + resulr);
Console.ReadKey();
}
}
第二种创建方式
class Program {
static void Main(string[] args) {
Task<int> task = Task.Run((() => {
Thread.Sleep(3000);
Console.WriteLine("Task Run Finish");
return 0;
}));
int resulr = task.Result;
Console.WriteLine("Task Result:" + resulr);
Console.ReadKey();
}
}
第三种创建方式
class Program {
static void Main(string[] args) {
Task<int> task = Task.Factory.StartNew((() => {
Thread.Sleep(3000);
Console.WriteLine("Task Run Finish");
return 0;
}));
Console.ReadKey();
}
}
第四种创建方式
创建Task还可以使用async/await的方式来实现。
class Program {
static void Main(string[] args) {
Task<int> task = GetAsyncResult();
int result = task.Result; // 阻塞主线程
task.Dispose();
Console.WriteLine("结果是:" + result);
}
private async static Task<int> GetAsyncResult() {
await Task.Delay(3000);
Console.WriteLine("Task Run Finish");
return 0;
}
}
使用task.Result方式同步获取结果会阻塞主线程。如果想要返回异步的结果,则使用如下方式:
private static Task<int> GetAsyncResult() {
Console.WriteLine("Task Run Finish");
return Task.FromResult(0);
}
组合任务-ContinueWith
任务并行
下面是一个简单的实例,task1开启子线程执行异步操作,主线程处理其他的操作。
class Program {
static void Main(string[] args) {
Task<int> task1 = new Task<int>((() => {
Console.WriteLine("使用Task执行异步操作.");
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}));
task1.Start();
Console.WriteLine("主线程执行其他操作");
Task task2 = task1.ContinueWith((task => {
Console.WriteLine("任务完成后的执行结果{0}", task.Result.ToString());
}));
task1.Wait();
task2.Wait();
}
}
输出结果如下:
主线程执行其他操作
使用Task执行异步操作.
任务完成后的执行结果4950
任务串行
创建四个任务,task1串行执行,task2和task3并行执行完毕后,task4串行执行。
class Program {
static void Main(string[] args) {
// Task1串行执行
Task task1 = Task.Factory.StartNew((() => {
Console.WriteLine("task1 Execute");
}));
// task2 和 task3并行执行
Task task2 = task1.ContinueWith((task => {
Console.WriteLine("task2 Execute");
}));
Task task3 = task1.ContinueWith((task => {
Console.WriteLine("task3 Execute");
}));
Task.WaitAll(task2, task3);
// task4 串行执行
Task task4 = Task.Factory.StartNew((() => {
Console.WriteLine("task4 Execute");
}));
task4.Wait();
}
}
输出结果如下:
task1 Execute
task3 Execute
task2 Execute
task4 Execute
父子任务
在父子任务中,TaskCreationOptions指定用于控制任务的创建和执行的可选行为的标志。
AttachedToParent:指定将任务附加到任务层次结构中的某个父级。
DenyChildAttach:指定任何尝试作为附加的子任务执行(即,使用AttachedToParent选项创建)的子任务都无法附加到父任务,会改成作为分离的子任务执行。
HideScheduler:防止环境计划程序被视为已创建任务的当前计划程序。
LongRunning:指定任务将是长时间运行的,粗粒度的操作,涉及比细化的系统更少、更强大的组件。
None:指定应使用默认行为。
PreferFairness:提示TaskScheduler以一种尽可能公平的方式安排任务
RunContinuationsAsynchronously:强制异步执行添加到当前任务的延续任务。
简单的代码演示如下:
class Program {
static void Main(string[] args) {
Task<int> parentTask = new Task<int>(state => {
Console.WriteLine(state);
// 创建并启动子任务
new Task((() => {
Console.WriteLine("task1 Execute");
}), TaskCreationOptions.AttachedToParent).Start();
new Task((() => {
Console.WriteLine("task2 Execute");
}), TaskCreationOptions.AttachedToParent).Start();
return 0;
}, "父任务,所有子任务完成后才执行");
// 任务完成后执行的操作
parentTask.ContinueWith((task => {
Console.WriteLine("Parent Task Finish");
}));
parentTask.Start();
Console.ReadKey();
}
}
输出结果如下:
父任务,所有子任务完成后才执行
task1 Execute
task2 Execute
Parent Task Finish
取消任务
class Program {
static void Main(string[] args) {
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
Task task = new Task((() => {
for (int i = 0; i < 100; i++) {
Thread.Sleep(1000);
Console.WriteLine("Task Execute{0}", i);
}
}), cancellationTokenSource.Token);
task.Start();
// 取消任务的运行
cancellationTokenSource.Cancel();
Console.ReadKey();
}
}
任务进度
使用IProgress实现异步编程的进程通知。
class Program {
static void Main(string[] args) {
Task task = displayProgress();
task.Wait();
}
private static async Task displayProgress() {
Progress<int> progress = new Progress<int>(percent => {
Console.WriteLine("{0}%", percent);
});
await Task.Run((() => DoProcessing(progress)));
Console.WriteLine("Finish");
}
private static void DoProcessing(IProgress<int> progress) {
for (int i = 0; i < 100; i++) {
Thread.Sleep(100);
progress.Report(i);
}
}
}
输出结果:
0%
1%
2%
3%
......
Finish