08、C# Task的使用

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

原文地址:https://www.cnblogs.com/pengjingya/p/14593143.html