C# Task的使用
Posted lgxlsm
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# Task的使用相关的知识,希望对你有一定的参考价值。
1.Task的优势
ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:
ThreadPool不支持线程的取消、完成、失败通知等交互性操作;
ThreadPool不支持线程执行的先后次序;
以往,如果开发者要实现上述功能,需要完成很多额外的工作,现在,FCL中提供了一个功能更强大的概念:Task。Task在线程池的基础上进行了优化,并提供了更多的API。在FCL4.0中,如果我们要编写多线程程序,Task显然已经优于传统的方式。
class Program static void Main(string[] args) Task t = new Task(() => Console.WriteLine("任务开始工作……"); //模拟工作过程 Thread.Sleep(5000); ); t.Start(); t.ContinueWith((task) => Console.WriteLine("任务完成,完成时候的状态为:"); Console.WriteLine("IsCanceled=0\tIsCompleted=1\tIsFaulted=2", task.IsCanceled, task.IsCompleted, task.IsFaulted); ); Console.ReadKey();
2.创建任务
class Program static void Main(string[] args) //创建任务 多种方式 //方式1 var t1 = new Task(() => TaskMethod("Task 1")); Console.WriteLine($"t1任务状态:t1.Status"); t1.Start(); Console.WriteLine($"t1任务状态:t1.Status"); Task.WaitAll(t1);//等待所有任务结束 Console.WriteLine($"t1任务状态:t1.Status"); Console.ReadKey(); //方式2 Task.Run(() => TaskMethod("Task 2")); //方式3 Task.Factory.StartNew(() => Console.WriteLine("Task 3")); static void TaskMethod(string str) Thread.Sleep(1000); Console.WriteLine(str);
class Program static void Main(string[] args) var t1 = new Task(() => TaskMethod("Task 1")); var t2 = new Task(() => TaskMethod("Task 2")); t2.Start(); t1.Start(); Task.WaitAll(t1, t2); Task.Run(() => TaskMethod("Task 3")); Task.Factory.StartNew(() => TaskMethod("Task 4")); //标记为长时间运行任务,则任务不会使用线程池,而在单独的线程中运行。 Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning); #region 常规的使用方式 Console.WriteLine("主线程执行业务处理."); //创建任务 Task task = new Task(() => Console.WriteLine("使用System.Threading.Tasks.Task执行异步操作."); for (int i = 0; i < 10; i++) Console.WriteLine(i); ); //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler) task.Start(); Console.WriteLine("主线程执行其他处理"); task.Wait(); #endregion Thread.Sleep(TimeSpan.FromSeconds(1)); Console.ReadLine(); static void TaskMethod(string name) Console.WriteLine("Task 0 is running on a thread id 1. Is thread pool thread: 2", name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
2.1 async/await的实现方式
无返回值
class Program public static void Main() Console.WriteLine("主线程执行业务处理."); AsyncFunction(); Console.WriteLine("主线程执行其他处理"); for (int i = 0; i < 10; i++) Console.WriteLine(($"Main:i=i")); Console.ReadLine(); async static void AsyncFunction() await Task.Delay(1); Console.WriteLine("使用System.Threading.Tasks.Task执行异步操作."); for (int i = 0; i < 10; i++) Console.WriteLine($"AsyncFunction:i=i");
有返回值
class Program static void Main(string[] args) TaskMethod("Main Thread Task"); Task<int> task = CreateTask("Task 1"); task.Start(); int result = task.Result; Console.WriteLine("Task 1 Result is: 0", result); task = CreateTask("Task 2"); //该任务会运行在主线程中 task.RunSynchronously(); result = task.Result; Console.WriteLine("Task 2 Result is: 0", result); task = CreateTask("Task 3"); Console.WriteLine(task.Status); task.Start(); while (!task.IsCompleted) Console.WriteLine(task.Status); Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine(task.Status); result = task.Result; Console.WriteLine("Task 3 Result is: 0", result); Console.ReadLine(); #region 常规使用方式 //创建任务 Task<int> getsumtask = new Task<int>(() => Getsum()); //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler) getsumtask.Start(); Console.WriteLine("主线程执行其他处理"); //等待任务的完成执行过程。 getsumtask.Wait(); //获得任务的执行结果 Console.WriteLine("任务执行结果:0", getsumtask.Result.ToString()); Console.ReadLine(); #endregion static Task<int> CreateTask(string name) return new Task<int>(() => TaskMethod(name)); static int TaskMethod(string name) Console.WriteLine("Task 0 is running on a thread id 1. Is thread pool thread: 2", name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); Thread.Sleep(TimeSpan.FromSeconds(2)); return 42; static int Getsum() int sum = 0; Console.WriteLine("使用Task执行异步操作."); for (int i = 0; i < 100; i++) sum += i; return sum;
阻塞主线程,等待返回结果
class Program public static void Main() var ret1 = AsyncGetsum(); Console.WriteLine("主线程执行其他处理"); for (int i = 1; i <= 3; i++) Console.WriteLine("Call Main()"); int result = ret1.Result; //阻塞主线程 Console.WriteLine("任务执行结果:0", result); Console.ReadLine(); static async Task<int> AsyncGetsum() await Task.Delay(1); int sum = 0; Console.WriteLine("使用Task执行异步操作."); for (int i = 0; i < 100; i++) sum += i; return sum;
3.组合任务 ContinueWith
class Program public static void Main() //创建一个任务 Task<int> task = new Task<int>(() => int sum = 0; Console.WriteLine("使用Task执行异步操作."); for (int i = 0; i < 100; i++) sum += i; return sum; ); //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler) task.Start(); Console.WriteLine("主线程执行其他处理"); //任务完成时执行处理。 task.ContinueWith(t => Console.WriteLine("任务完成后的执行结果:0", t.Result.ToString()); ); Console.ReadLine();
任务的串行
class Program static void Main(string[] args) ConcurrentStack<int> stack = new ConcurrentStack<int>(); //t1先串行 var t1 = Task.Factory.StartNew(() => stack.Push(1); stack.Push(2); ); //t2,t3并行执行 var t2 = t1.ContinueWith(t => stack.TryPop(out var result); Console.WriteLine("Task t2 result=0,Thread id 1", result, Thread.CurrentThread.ManagedThreadId); ); //t2,t3并行执行 var t3 = t1.ContinueWith(t => stack.TryPop(out var result); Console.WriteLine("Task t3 result=0,Thread id 1", result, Thread.CurrentThread.ManagedThreadId); ); //等待t2和t3执行完 Task.WaitAll(t2, t3); //t7串行执行 var t4 = Task.Factory.StartNew(() => Console.WriteLine("当前集合元素个数:0,Thread id 1", stack.Count, Thread.CurrentThread.ManagedThreadId); ); t4.Wait(); Console.ReadLine();
子任务
class Program static void Main(string[] args) WaitForSimpleNestedTask(); Console.WriteLine("====================================================="); SimpleNestedTask(); Thread.SpinWait(600000);//等待SimpleNestedTask结束 再运行 Console.WriteLine("====================================================="); SimpleNestedTaskAttachedToParent(); Console.Read(); static void WaitForSimpleNestedTask() var outer = Task<int>.Factory.StartNew(() => Console.WriteLine("Outer1 task executing."); var nested = Task<int>.Factory.StartNew(() => Console.WriteLine("Nested1 task starting."); Thread.SpinWait(5000000); Console.WriteLine("Nested1 task completing."); return 42; ); // 父任务等待子任务完成 return nested.Result; ); //输出的时候使用了返回值outer.Result,所以自动等待父任务完成 Console.WriteLine("Outer1 has returned 0.", outer.Result); static void SimpleNestedTask() var parent = Task.Factory.StartNew(() => Console.WriteLine("Outer2 task executing."); var child = Task.Factory.StartNew(() => Console.WriteLine("Nested2 task starting."); Thread.SpinWait(500000); Console.WriteLine("Nested2 task completing."); ); ); //等待父任务完成,再输出字符串,如果没有这个等待,这句话会先输出 parent.Wait(); Console.WriteLine("Outer2 has completed."); static void SimpleNestedTaskAttachedToParent() var parent = Task.Factory.StartNew(() => Console.WriteLine("Outer3 task executing."); var child = Task.Factory.StartNew(() => Console.WriteLine("Nested3 task starting."); Thread.SpinWait(500000); Console.WriteLine("Nested3 task completing."); , TaskCreationOptions.AttachedToParent); //增加参数TaskCreationOptions.AttachedToParent,将子任务附加进父任务,这样子任务完成后,父任务才算完成。 ); //等待父任务完成,再输出字符串,如果没有这个等待,这句话会先输出 parent.Wait(); Console.WriteLine("Outer has completed.");
动态并行,父任务等待所有子任务完成后 整个任务才算完成
class Node public Node Left get; set; public Node Right get; set; public string Text get; set; class Program static Node GetNode() Node root = new Node Left = new Node Left = new Node Text = "L-L" , Right = new Node Text = "L-R" , Text = "L" , Right = new Node Left = new Node Text = "R-L" , Right = new Node Text = "R-R" , Text = "R" , Text = "Root" ; return root; static void Main(string[] args) Node root = GetNode(); DisplayTree(root); Console.ReadLine(); static void DisplayTree(Node root) var task = Task.Factory.StartNew(() => DisplayNode(root), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); task.Wait(); static void DisplayNode(Node current) if (current.Left != null) Task.Factory.StartNew(() => DisplayNode(current.Left), CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Default); if (current.Right != null) Task.Factory.StartNew(() => DisplayNode(current.Right), CancellationToken.None, TaskCreationOptions.AttachedToParent, TaskScheduler.Default); Console.WriteLine("当前节点的值为0;处理的ThreadId=1", current.Text, Thread.CurrentThread.ManagedThreadId);
取消任务 CancellationTokenSource
class Program private static int TaskMethod(string name, int seconds, CancellationToken token) Console.WriteLine("Task 0 is running on a thread id 1. Is thread pool thread: 2", name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); for (int i = 0; i < seconds; i++) Thread.Sleep(TimeSpan.FromSeconds(1)); if (token.IsCancellationRequested) return -1; return 42 * seconds; private static void Main(string[] args) var cts = new CancellationTokenSource(); var longTask = new Task<int>(() => TaskMethod("Task 1", 10, cts.Token), cts.Token); Console.WriteLine(longTask.Status); cts.Cancel(); Console.WriteLine(longTask.Status); Console.WriteLine("First task has been cancelled before execution"); cts = new CancellationTokenSource(); longTask = new Task<int>(() => TaskMethod("Task 2", 10, cts.Token), cts.Token); longTask.Start(); for (int i = 0; i < 5; i++) Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine(longTask.Status); cts.Cancel(); for (int i = 0; i < 5; i++) Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine(longTask.Status); Console.WriteLine("A task has been completed with result 0.", longTask.Result); Console.ReadLine();
4.处理任务中的异常
单个任务
class Program static int TaskMethod(string name, int seconds) Console.WriteLine("Task 0 is running on a thread id 1. Is thread pool thread: 2", name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); Thread.Sleep(TimeSpan.FromSeconds(seconds)); throw new Exception("Boom!"); return 42 * seconds; static void Main(string[] args) try Task<int> task = Task.Run(() => TaskMethod("Task 2", 2)); int result = task.GetAwaiter().GetResult(); Console.WriteLine("Result: 0", result); catch (Exception ex) Console.WriteLine("Task 2 Exception caught: 0", ex.Message); Console.WriteLine("----------------------------------------------"); Console.ReadLine();
多个任务
class Program static int TaskMethod(string name, int seconds) Console.WriteLine("Task 0 is running on a thread id 1. Is thread pool thread: 2", name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); Thread.Sleep(TimeSpan.FromSeconds(seconds)); throw new Exception(string.Format("Task 0 Boom!", name)); return 42 * seconds; public static void Main(string[] args) try var t1 = new Task<int>(() => TaskMethod("Task 3", 3)); var t2 = new Task<int>(() => TaskMethod("Task 4", 2)); var complexTask = Task.WhenAll(t1, t2); var exceptionHandler = complexTask.ContinueWith(t => Console.WriteLine("Result: 0", t.Result), TaskContinuationOptions.OnlyOnFaulted ); t1.Start(); t2.Start(); Task.WaitAll(t1, t2); catch (AggregateException ex) // enumerate the exceptions that have been aggregated foreach (Exception inner in ex.InnerExceptions) Console.WriteLine($"Exception type inner.GetType() from inner.Source ex.Message"); Console.ReadLine(); Console.WriteLine("----------------------");
async/await的方式
class Program static async Task ThrowNotImplementedExceptionAsync() throw new NotImplementedException(); static async Task ThrowInvalidOperationExceptionAsync() throw new InvalidOperationException(); static async Task Normal() await Fun(); static Task Fun() return Task.Run(() => for (int i = 1; i <= 10; i++) Console.WriteLine("i=0", i); Thread.Sleep(200); ); static async Task ObserveOneExceptionAsync() var task1 = ThrowNotImplementedExceptionAsync(); var task2 = ThrowInvalidOperationExceptionAsync(); var task3 = Normal(); try //异步的方式 Task allTasks = Task.WhenAll(task1, task2, task3); await allTasks; //同步的方式 //Task.WaitAll(task1, task2, task3); catch (NotImplementedException ex) Console.WriteLine("task1 任务报错!"); catch (InvalidOperationException ex) Console.WriteLine("task2 任务报错!"); catch (Exception ex) Console.WriteLine("任务报错!"); public static void Main() Task task = ObserveOneExceptionAsync(); Console.WriteLine("主线程继续运行........"); task.Wait(); Console.ReadLine();
5.Task.FromResult的应用
class Program static IDictionary<string, string> cache = new Dictionary<string, string>() "0001","A", "0002","B", "0003","C", "0004","D", "0005","E", "0006","F", ; public static void Main() Task<string> task = GetValueFromCache("0006"); Console.WriteLine("主程序继续执行。。。。"); string result = task.Result; Console.WriteLine("result=0", result); Console.ReadLine(); private static Task<string> GetValueFromCache(string key) Console.WriteLine("GetValueFromCache开始执行。。。。"); Thread.Sleep(5000); Console.WriteLine("GetValueFromCache继续执行。。。。"); if (cache.TryGetValue(key, out var result)) return Task.FromResult(result); return Task.FromResult("");
6.使用IProgress实现异步编程的进程通知
IProgress<in T>只提供了一个方法void Report(T value),通过Report方法把一个T类型的值报告给IProgress,然后IProgress<in T>的实现类Progress<in T>的构造函数接收类型为Action<T>的形参,通过这个委托让进度显示在UI界面中
class Program static void DoProcessing(IProgress<int> progress) for (int i = 0; i <= 100; ++i) Thread.Sleep(100); progress?.Report(i); static async Task Display() //当前线程 var progress = new Progress<int>(percent => Console.Clear(); Console.Write($"percent%"); ); //线程池线程 await Task.Run(() => DoProcessing(progress)); Console.WriteLine(""); Console.WriteLine("结束"); public static void Main() Task task = Display(); task.Wait();
7.Factory.FromAsync的应用 (简APM模式(委托)转换为任务)(BeginXXX和EndXXX)
带回调方式的带回调方式的
class Program private delegate string AsynchronousTask(string threadName); private static string Test(string threadName) Console.WriteLine("Starting..."); Console.WriteLine("Is thread pool thread: 0", Thread.CurrentThread.IsThreadPoolThread); Thread.Sleep(TimeSpan.FromSeconds(2)); Thread.CurrentThread.Name = threadName; return string.Format("Thread name: 0", Thread.CurrentThread.Name); private static void Callback(IAsyncResult ar) Console.WriteLine("Starting a callback..."); Console.WriteLine("State passed to a callbak: 0", ar.AsyncState); Console.WriteLine("Is thread pool thread: 0", Thread.CurrentThread.IsThreadPoolThread); Console.WriteLine("Thread pool worker thread id: 0", Thread.CurrentThread.ManagedThreadId); //执行的流程是 先执行Test--->Callback--->task.ContinueWith static void Main(string[] args) AsynchronousTask d = Test; Console.WriteLine("Option 1"); Task<string> task = Task<string>.Factory.FromAsync( d.BeginInvoke("AsyncTaskThread", Callback, "a delegate asynchronous call"), d.EndInvoke); task.ContinueWith(t => Console.WriteLine("Callback is finished, now running a continuation! Result: 0", t.Result)); while (!task.IsCompleted) Console.WriteLine(task.Status); Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine(task.Status); Console.ReadLine();
不带回调方式的
class Program private delegate string AsynchronousTask(string threadName); private static string Test(string threadName) Console.WriteLine("Starting..."); Console.WriteLine("Is thread pool thread: 0", Thread.CurrentThread.IsThreadPoolThread); Thread.Sleep(2000); Thread.CurrentThread.Name = threadName; return string.Format("Thread name: 0", Thread.CurrentThread.Name); //执行的流程是 先执行Test--->task.ContinueWith static void Main(string[] args) AsynchronousTask d = Test; Task<string> task = Task<string>.Factory.FromAsync( d.BeginInvoke, d.EndInvoke, "AsyncTaskThread", "a delegate asynchronous call"); task.ContinueWith(t => Console.WriteLine("Task is completed, now running a continuation! Result: 0", t.Result)); while (!task.IsCompleted) Console.WriteLine(task.Status); Thread.Sleep(TimeSpan.FromSeconds(0.5)); Console.WriteLine(task.Status); Console.ReadLine();
以上是关于C# Task的使用的主要内容,如果未能解决你的问题,请参考以下文章
Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题