您的位置:首页 > 编程语言 > C#

Task C# 多线程和异步模型 TPL模型 【C#】43. TPL基础——Task初步 22 C# 第十八章 TPL 并行编程 TPL 和传统 .NET 异步编程一 Task.Delay() 和 Thread.Sleep() 区别

2018-05-23 15:38 921 查看

Task C# 多线程和异步模型 TPL模型

 

Task,异步,多线程简单总结

 

1,如何把一个异步封装为Task异步

Task.Factory.FromAsync

对老的一些异步模型封装为Task

TaskCompletionSource

更通用,在回调中只要SetResult()一下就表示Task结束了,用它可以将各种异步回调封装为Task

 

2,一个可以await的可以返回Task的Async结尾的异步方法从哪里开始进入另一个线程的

如果是对BeginXXX EndXXX的APM异步模型封装的,从进入XXXAsync方法后直到BeginXXX前还在调用方法的线程内,然后无阻塞的等待回调,当等打完后,就相当于在回调中了,此时可能进入了另一个线程,其他也是类似的,从底层方法异步的地方开始异步的,而不是一进入方法就到了另一个线程了,所以进入方法后写很多CPU密集程序是会阻塞的

 

3,如何立刻扔到另一个线程

Task.Run或者Task.Factory.StartNew可以直接从另一个线程开始,可以直接把XXXAsync方法扔进去

 

4,一个约定

纯C#框架或者类库提供的Async结尾的可await方法,里面一定有无阻塞异步的实现,否则没必要搞成异步的,就比如newtonsoft.json的异步序列化方法被标识为已过时。

 

5,Task外面如何告诉Task该取消了

CancellationTokenSource

其实和一个Flag差不多,只不过封装了一些方法以异常类

 

6,很多情况下要先学会同步才能并发

 

7,Task.Run Task.Start Task.Factory.StartNew 等都是使用线程池的线程

 

8,IO异步底层为IRP消息,通常是和硬件交互所使用的消息机制,当然那是驱动层的事情,IO异步当然也就是无阻塞的,等IRP消息回来就是回调

 

9,UI线程

最终会渲染界面的代码一定要在UI线程执行,可以使用比如winform的control.Invoke ,wpf的Dispatcher

 

一旦开始并发了,还有很多很多的坑

 

【C#】43. TPL基础——Task初步 2016年12月11日 20:56:10 阅读数:1379

从这篇文章开始,我想直接进入关于Task的一些内容,有时间再回顾Threadpool的相关内容。

我一开始接触Task就觉得他和Thread很像,都是开新的线程。但是两者有很多区别,其中比较明显的是:Task创建的是线程池任务,而Thread默认创建的是前台任务。

    同Thread一样,Task可以使用lambda表达式来构造action,作为Task的构造函数参数。如下:

 

1、先定义一个函数TaskMethod,他接受name作为字符串参数。

 

[csharp] view plain copy  
  1. static void TaskMethod(string name)  
  2. {  
  3. Console.WriteLine("Task {0} 运行在线程id为{1}的线程上. 是否是线程池中线程?:{2}",  
  4. name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);  
  5. }  

2、新建Task实例,并且Start():

 

[csharp] view plain copy  
  1. var t1 = new Task(() => TaskMethod("Task 1"));  
  2. t1.Start();  

因为Task接受无参数和返回值的Action作为构造器参数,因此此处可以使用如上的lambda表达式,在表达式中传参"Task 1"。

运行结果如下:

可以很明显的看出,该Task(t1)运行在线程池中。

必须要指出的是,线程池一般只运行执行时间较短的异步操作,需要长时间执行的操作尽量不要使用线程池。

除了上面这种开Task的方法,还有两种常见的用法,分别是 Task.Run() 和 Task.Factory.StartNew()。两者的区别在于后者能传入一些额外参数,以丰富Task的运行选项。例如:

 

[csharp] view plain copy  
  1. Task.Run(() => TaskMethod("Task 3"));  
  2. Task.Factory.StartNew(() => TaskMethod("Task 4"));  
  3. Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);  

 

第一句直接调用静态方法Task.Run(),lambda表达式作为参数(Action),不需要再调用Start()方法,立即执行。

第二句则使用了Task.Factory.StartNew()默认方法,最后一句加入了选项“LongRunning”,意味着该任务将长时间运行,因此他不是在线程池中执行。

结果如下:

注意:Task的运行有一定的随机性,开始次序会有变化!

 

 

 

22 C# 第十八章 TPL 并行编程 2013年08月12日 09:24:08 阅读数:5175 C# TPL(Task Parallel Library) 并行编程是.Net4 为多线程编程引入的新的API。因为并行编程涉及到的知识非常广泛,这里只是简单的把.Net 4中TPL的用法总结了一下。

一: Task 线程的基本使用


关于Action委托: 它是 .Net 定义的一种委托类型。
public delegate void Action():  封装一个方法,该方法不具有参数并且不返回值。
public delegate void Action<in T>(T obj):  封装一个方法,该方法只有一个参数并且不返回值。
...  ...


可以使用此委托以参数形式传递方法,而不用显式声明自定义的委托。 封装的方法必须与此委托定义的方法签名相对应。
http://msdn.microsoft.com/zh-cn/library/vstudio/system.action.aspx


光看语言描述理解起来有点费劲。看MSDN给的例子,把自定义的委托和Action比较一下就清楚多了。


两个小例子:

使用委托的例子

 

[csharp] view plain copy  
  1. using System;  
  2. using System.Windows.Forms;  
  3.   
  4.   
  5. public delegate void ShowValue();  
  6.   
  7.   
  8. public class Name  
  9. {  
  10.    private string instanceName;  
  11.   
  12.   
  13.    public Name(string name)  
  14.    {  
  15.       this.instanceName = name;  
  16.    }  
  17.   
  18.   
  19.    public void DisplayToConsole()  
  20.    {  
  21.       Console.WriteLine(this.instanceName);  
  22.    }  
  23.   
  24.   
  25.    public void DisplayToWindow()  
  26.    {  
  27.       MessageBox.Show(this.instanceName);  
  28.    }  
  29. }  
  30.   
  31.   
  32. public class testTestDelegate  
  33. {  
  34.    public static void Main()  
  35.    {  
  36.       Name testName = new Name("Koani");  
  37.       ShowValue showMethod = testName.DisplayToWindow;  
  38.       showMethod();  
  39.    }  
  40. }  


 



使用 action的例子

 

[csharp] view plain copy  
  1. using System;  
  2. using System.Windows.Forms;  
  3.   
  4.   
  5. public class Name  
  6. {  
  7.    private string instanceName;  
  8.   
  9.   
  10.    public Name(string name)  
  11.    {  
  12.       this.instanceName = name;  
  13.    }  
  14.   
  15.   
  16.    public void DisplayToConsole()  
  17.    {  
  18.       Console.WriteLine(this.instanceName);  
  19.    }  
  20.   
  21.   
  22.    public void DisplayToWindow()  
  23.    {  
  24.       MessageBox.Show(this.instanceName);  
  25.    }  
  26. }  
  27.   
  28.   
  29. public class testTestDelegate  
  30. {  
  31.    public static void Main()  
  32.    {  
  33.       Name testName = new Name("Koani");  
  34.       Action showMethod = testName.DisplayToWindow;  
  35.       showMethod();  
  36.    }  
  37. }  

这里把Action 看成了一种特殊的委托,没有参数也没有返回值,这样理解起来就简单了。

 




关于 Task 的简单介绍
更确切的说它是把线程的概念抽象出来了。这里它更关心的是线程中工作的内容。而把创建线程,重用线程或释放线程等平台相关的工作留给了系统。它更贴近与System.Threading.ThreadPool。一个由系统管理的ThreadPool。


使用 Task 创建线程

 一个简单的线程使用的例子

http://msdn.microsoft.com/zh-cn/library/vstudio/1h2f2459.aspx

 

[csharp] view plain copy  
  1. using System;  
  2. using System.Threading;  
  3. using System.Threading.Tasks;  
  4.   
  5. class StartNewDemo  
  6. {  
  7.     static void Main()  
  8.     {  
  9.         Action<object> action = (object obj) =>  
  10.         {  
  11.             Console.WriteLine("Task={0}, obj={1}, Thread={2}", Task.CurrentId, obj.ToString(), Thread.CurrentThread.ManagedThreadId);  
  12.         };  
  13.   
  14.         // Construct an unstarted task  
  15.         Task t1 = new Task(action, "alpha");  
  16.   
  17.         // Cosntruct a started task  
  18.         Task t2 = Task.Factory.StartNew(action, "beta");  
  19.   
  20.         // Block the main thread to demonstate that t2 is executing  
  21.         t2.Wait();  
  22.   
  23.         // Launch t1   
  24.         t1.Start();  
  25.   
  26.         Console.WriteLine("t1 has been launched. (Main Thread={0})", Thread.CurrentThread.ManagedThreadId);  
  27.   
  28.         // Wait for the task to finish.  
  29.         // You may optionally provide a timeout interval or a cancellation token  
  30.         // to mitigate situations when the task takes too long to finish.  
  31.         t1.Wait();  
  32.   
  33.         // Construct an unstarted task  
  34.         Task t3 = new Task(action, "gamma");  
  35.   
  36.         // Run it synchronously  
  37.         t3.RunSynchronously();  
  38.   
  39.         // Although the task was run synchrounously, it is a good practice to wait for it which observes for   
  40.         // exceptions potentially thrown by that task.  
  41.         t3.Wait();  
  42.   
  43.         Console.ReadKey();  
  44.     }  
  45. }  


 

程序说明: 


API: Task.Factory
     创建任务并立即启动任务。
     创建在一组任务中的任意或全部任务完成后启动的任务延续项
     创建表示开始/结束方法对的任务,这些方法采用异步编程模型。




Task.RunSynchronously
     任务只可以启动并运行一次。 再次计划任务的尝试将导致异常。通过 RunSynchronously 执行的任务将与当前 TaskScheduler 关联。
     如果目标计划程序不支持当前线程上运行此任务,则在计划程序上计划执行该任务,当前线程会阻塞,直到该任务完成执行。




从线程中返回结果


线程常用的成员
Status: 此任务实例的当前状态 TaskStatus(http://msdn.microsoft.com/zh-cn/library/vstudio/system.threading.tasks.taskstatus.aspx)


IsCompleted: true ,如果任务已完成;否则 false。它不管任务是否出错。

 

Id: System.Int32类型, 系统分配给此任务实例的一个整数。任务 ID 分配在需要时并不一定表示在任务实例创建的顺序。

 

AsyncState  它允许跟踪额外的数据。例如,假定多个任务要计算一个List<T>的值。为了将结果放到列表中正确的位置,一个办法是将准备包含结果的那个列表索引存储到AsyncState中。这样一来,在任务结束后,代码可使用AsyncState先转型为int 访问列表中特定索引的位置。这个理解的还不是很透彻,回头应该找个例子看看。

ContinueWith(): 创建一个在目标 Task 完成时异步执行的延续任务。


一个实例
[csharp] view plain copy  
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Threading;  
  4. using System.Threading.Tasks;  
  5.   
  6. // Demonstrates how to associate state with task continuations.  
  7. class ContinuationState  
  8. {  
  9.     // Simluates a lengthy operation and returns the time at which  
  10.     // the operation completed.  
  11.     public static DateTime DoWork()  
  12.     {  
  13.         // Simulate work by suspending the current thread   
  14.         // for two seconds.  
  15.         Console.WriteLine("Thread = {0} sleep 2 seconds", Task.CurrentId);  
  16.         Thread.Sleep(2000);  
  17.   
  18.         // Return the current time.  
  19.         return DateTime.Now;  
  20.     }  
  21.   
  22.     static void Main(string[] args)  
  23.     {  
  24.         Action action = () =>  
  25.         {  
  26.             DoWork();  
  27.         };  
  28.   
  29.         Task<DateTime> t = new Task<DateTime>(DoWork);  
  30.         t.Start();  
  31.           
  32.         Console.WriteLine("Date = {0}", t.Result);  
  33.         t.Wait();  
  34.         Console.ReadKey();  
  35.     }  
  36. }  



二: Task上的未处理异常

关注点是如何从一个不同的线程中引发的未处理的异常。在Task执行期间产生的未处理的异常会被禁止(suppressed),直到调用某个任务完成(task complete)成员: Wait(), Result, Task.WaitAll(),或者Task.WaitAny()。上述每个成员都会引发任务执行期间发生的任何未处理的异常。


Wait引发异常的例子


[csharp] view plain copy  
  1. using System;  
  2. using System.Threading;  
  3. using System.Threading.Tasks;  
  4.   
  5. namespace Thread_Task_Sample_Exception_Simple  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             Action<object> action = (object obj) =>  
  12.             {  
  13.                 Console.WriteLine("Task={0}, obj={1}, Thread={2}  Throw Exception", Task.CurrentId, obj.ToString(), Thread.CurrentThread.ManagedThreadId);  
  14.   
  15.                 throw (new Exception());  
  16.             };  
  17.   
  18.   
  19.             // Cosntruct a started task  
  20.             Task t = Task.Factory.StartNew(action, "A");  
  21.   
  22.   
  23.             for (int i = 0; i < 50; i++)  
  24.             {  
  25.                 Console.Write(".");  
  26.                 Thread.Sleep(200);  
  27.             }  
  28.             Console.WriteLine();  
  29.   
  30.             try  
  31.             {  
  32.                 // Block the main thread to demonstate that t2 is executing  
  33.                 t.Wait();  
  34.             }  
  35.             catch (Exception ex)  
  36.             {  
  37.                 Console.WriteLine("Task.Wait cause an exception, We caught it");  
  38.             }  
  39.   
  40.             Console.ReadKey();  
  41.         }  
  42.     }  
  43. }  



使用 ContinueWith ()

[csharp] view plain copy  
  1. using System;  
  2. using System.Threading;  
  3. using System.Threading.Tasks;  
  4.   
  5. class ContinuationSimpleDemo  
  6. {  
  7.     // Demonstrated features:  
  8.     //      Task.Factory  
  9.     //      Task.ContinueWith()  
  10.     //      Task.Wait()  
  11.     // Expected results:  
  12.     //      A sequence of three unrelated tasks is created and executed in this order - alpha, beta, gamma.  
  13.     //      A sequence of three related tasks is created - each task negates its argument and passes is to the next task: 5, -5, 5 is printed.  
  14.     //      A sequence of three unrelated tasks is created where tasks have different types.  
  15.     // Documentation:  
  16.     static void Main()  
  17.     {  
  18.         Action<string> action =  
  19.             (str) =>  
  20.                 Console.WriteLine("Task={0}, str={1}, Thread={2}", Task.CurrentId, str, Thread.CurrentThread.ManagedThreadId);  
  21.   
  22.         // Creating a sequence of action tasks (that return no result).  
  23.         Console.WriteLine("Creating a sequence of action tasks (that return no result)");  
  24.         Task.Factory.StartNew(() => action("alpha"))  
  25.             .ContinueWith(antecendent => action("beta"))        // Antecedent data is ignored  
  26.             .ContinueWith(antecendent => action("gamma"))  
  27.             .Wait();  
  28.   
  29.   
  30.         Func<int, int> negate =  
  31.             (n) =>  
  32.             {  
  33.                 Console.WriteLine("Task={0}, n={1}, -n={2}, Thread={3}", Task.CurrentId, n, -n, Thread.CurrentThread.ManagedThreadId);  
  34.                 return -n;  
  35.             };  
  36.   
  37.         // Creating a sequence of function tasks where each continuation uses the result from its antecendent  
  38.         Console.WriteLine("\nCreating a sequence of function tasks where each continuation uses the result from its antecendent");  
  39.         Task<int>.Factory.StartNew(() => negate(5))  
  40.             .ContinueWith(antecendent => negate(antecendent.Result))     // Antecedent result feeds into continuation  
  41.             .ContinueWith(antecendent => negate(antecendent.Result))  
  42.             .Wait();  
  43.   
  44.   
  45.         // Creating a sequence of tasks where you can mix and match the types  
  46.         Console.WriteLine("\nCreating a sequence of tasks where you can mix and match the types");  
  47.         Task<int>.Factory.StartNew(() => negate(6))  
  48.             .ContinueWith(antecendent => action("x"))  
  49.             .ContinueWith(antecendent => negate(7))  
  50.             .Wait();  
  51.   
  52.         Console.ReadKey();  
  53.     }  
  54. }  

使用ContinueWith 如果在程序中出现异常,后面后续的处理函数可以帮助程序把信息清理干净。



三: 取消任务

野蛮终止的问题:在.Net3.5 或之前的版本,其实并没有怎么支持线程的取消,相反采取的是一种中断的方式。.Net4 中基于PLINQ和TPL的API只支持一种取消请求的方式,协作式取消 -- 目标Task可自行决定是否满足取消请求。


取消任务的一个实例代码:

 

[csharp] view plain copy  
  1. using System;  
  2. using System.Collections.Concurrent;  
  3. using System.Threading;  
  4. using System.Threading.Tasks;  
  5.   
  6. public class Example  
  7. {  
  8.     public static void Main()  
  9.     {  
  10.         var tokenSource = new CancellationTokenSource();  
  11.         var token = tokenSource.Token;  
  12.   
  13.         // Store references to the tasks so that we can wait on them and     
  14.         // observe their status after cancellation.   
  15.         Task t;  
  16.         var tasks = new ConcurrentBag<Task>();  
  17.   
  18.         Console.WriteLine("Press any key to begin tasks...");  
  19.         Console.WriteLine("To terminate the example, press 'c' to cancel and exit...");  
  20.         Console.ReadKey();  
  21.         Console.WriteLine();  
  22.   
  23.         // Request cancellation of a single task when the token source is canceled.    
  24.         // Pass the token to the user delegate, and also to the task so it can     
  25.         // handle the exception correctly.  
  26.         t = Task.Factory.StartNew(() => DoSomeWork(1, token), token);  
  27.         Console.WriteLine("Task {0} executing", t.Id);  
  28.         tasks.Add(t);  
  29.   
  30.         // Request cancellation of a task and its children. Note the token is passed    
  31.         // to (1) the user delegate and (2) as the second argument to StartNew, so     
  32.         // that the task instance can correctly handle the OperationCanceledException.  
  33.         t = Task.Factory.StartNew(() =>  
  34.         {  
  35.             // Create some cancelable child tasks.    
  36.             Task tc;  
  37.             for (int i = 3; i <= 10; i++)  
  38.             {  
  39.                 // For each child task, pass the same token    
  40.                 // to each user delegate and to StartNew.  
  41.                 tc = Task.Factory.StartNew(iteration => DoSomeWork((int)iteration, token), i, token);  
  42.                 Console.WriteLine("Task {0} executing", tc.Id);  
  43.                 tasks.Add(tc);  
  44.                 // Pass the same token again to do work on the parent task.     
  45.                 // All will be signaled by the call to tokenSource.Cancel below.  
  46.                 DoSomeWork(2, token);  
  47.             }  
  48.         }, token);  
  49.   
  50.         Console.WriteLine("Task {0} executing", t.Id);  
  51.         tasks.Add(t);  
  52.   
  53.         // Request cancellation from the UI thread.    
  54.         if (Console.ReadKey().KeyChar == 'c')  
  55.         {  
  56.             tokenSource.Cancel();  
  57.             Console.WriteLine("\nTask cancellation requested.");  
  58.   
  59.             // Optional: Observe the change in the Status property on the task.    
  60.             // It is not necessary to wait on tasks that have canceled. However,    
  61.             // if you do wait, you must enclose the call in a try-catch block to    
  62.             // catch the TaskCanceledExceptions that are thrown. If you do     
  63.             // not wait, no exception is thrown if the token that was passed to the     
  64.             // StartNew method is the same token that requested the cancellation.   
  65.         }  
  66.   
  67.         try  
  68.         {  
  69.             Task.WaitAll(tasks.ToArray());  
  70.         }  
  71.         catch (AggregateException e)  
  72.         {  
  73.             Console.WriteLine("\nAggregateException thrown with the following inner exceptions:");  
  74.             // Display information about each exception.    
  75.             foreach (var v in e.InnerExceptions)  
  76.             {  
  77.                 if (v is TaskCanceledException)  
  78.                     Console.WriteLine("   TaskCanceledException: Task {0}",  
  79.                                       ((TaskCanceledException)v).Task.Id);  
  80.                 else  
  81.                     Console.WriteLine("   Exception: {0}", v.GetType().Name);  
  82.             }  
  83.             Console.WriteLine();  
  84.         }  
  85.   
  86.         // Display status of all tasks.    
  87.         foreach (var task in tasks)  
  88.             Console.WriteLine("Task {0} status is now {1}", task.Id, task.Status);  
  89.   
  90.   
  91.         Console.ReadKey();  
  92.     }  
  93.   
  94.     static void DoSomeWork(int taskNum, CancellationToken ct)  
  95.     {  
  96.         // Was cancellation already requested?    
  97.         if (ct.IsCancellationRequested == true)  
  98.         {  
  99.             Console.WriteLine("Task {0} was cancelled before it got started.", taskNum);  
  100.             ct.ThrowIfCancellationRequested();  
  101.         }  
  102.   
  103.         int maxIterations = 100;  
  104.   
  105.         // NOTE!!! A "TaskCanceledException was unhandled    
  106.         // by user code" error will be raised here if "Just My Code"   
  107.         // is enabled on your computer. On Express editions JMC is    
  108.         // enabled and cannot be disabled. The exception is benign.    
  109.         // Just press F5 to continue executing your code.    
  110.         for (int i = 0; i <= maxIterations; i++)  
  111.         {  
  112.             // Do a bit of work. Not too much.    
  113.             var sw = new SpinWait();  
  114.             for (int j = 0; j <= 100; j++)  
  115.                 sw.SpinOnce();  
  116.   
  117.             if (ct.IsCancellationRequested)  
  118.             {  
  119.                 Console.WriteLine("Task {0} cancelled", taskNum);  
  120.                 ct.ThrowIfCancellationRequested();  
  121.             }  
  122.         }  
  123.     }  
  124. }  




四: 并行迭代  Task.Parallel


API 会判定同时执行多少个线程效率最高。效率由一个爬山算法来决定。

一个parallel并行的例子

[csharp] view plain copy  
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Threading.Tasks;  
  4. using System.Text;  
  5.   
  6. namespace Thread_Task_Sample_Parallel  
  7. {  
  8.     class Program  
  9.     {  
  10.         static void Main(string[] args)  
  11.         {  
  12.             int[] data = new int[100];  
  13.             int i = 0;  
  14.   
  15.             for (i = 0; i < data.Length; i++)  
  16.             {  
  17.                 data[i] = i;  
  18.                 Console.Write("{0} ", data[i]);  
  19.             }  
  20.             Console.WriteLine(" \n ");  
  21.   
  22.             Console.WriteLine("\nParallel running ... ... \n");  
  23.             Parallel.For(0, 100, (j)=>  
  24.                 {  
  25.                     System.Threading.Thread.Sleep(1000);  
  26.                     data[j] = data[j] * 2;  
  27.                     Console.Write("{0} ", data[j]);  
  28.                 });  
  29.             Console.WriteLine("\n\nParallel end ... ... \n");  
  30.   
  31.   
  32.             Console.WriteLine(" \n ");  
  33.             for (i = 0; i < data.Length; i++)  
  34.             {  
  35.                 Console.Write("{0} ", data[i]);  
  36.             }  
  37.             Console.WriteLine(" \n ");  
  38.   
  39.             Console.ReadKey();  
  40.         }  
  41.     }  
  42. }  

测试结果:




 

实例代码

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangzhiyu1980/article/details/9915431  
TPL 和传统 .NET 异步编程一 2010年05月28日 17:47:00 阅读数:3715

TPL即任务并行库,是.NET Framework 4版本中的新鲜物,是System.Threading 和 System.Threading.Tasks 命名空间中的一组公共类型和 API。TPL 的目的在于简化向应用程序中添加并行性和并发性的过程,从而提高开发人员的工作效率。 TPL 会动态地按比例调节并发程度,以便最有效地使用所有可用的处理器。此外,TPL 还处理工作分区、ThreadPool 上的线程调度、取消支持、状态管理以及其他低级别的细节操作。通过使用 TPL,您可以在将精力集中于程序要完成的工作,同时最大程度地提高代码的性能。

 

TPL涉及的内容很多,在这里主要先介绍一下TPL与传统的.NET异步编程之间的关系。在以前的.NET Framework版本中,它 提供以下两种执行 I/O 绑定和计算绑定异步操作的标准模式:

  • 异步编程模型 (APM),在该模型中异步操作由一对 Begin/End 方法(如 FileStream.BeginRead 和 Stream.EndRead)表示。
  • 基于事件的异步模式 (EAP),在该模式中异步操作由名为“操作名称Async”和“操作名称Completed”的方法/事件对(例如 WebClient.DownloadStringAsync 和 WebClient.DownloadStringCompleted)表示。(EAP 是在 .NET Framework 2.0 版中引入的)。

 

而任务并行库 (TPL) 可通过各种方式与任一异步模式结合使用。可以将 APM 和 EAP 操作以 Task 的形式向库使用者公开,或者可以公开 APM 模式,但使用 Task 对象在内部实现它们。在这两种方案中,通过使用 Task 对象,可以简化代码并利用以下有用的功能:

  • 在任务启动后,可以随时以任务延续的形式注册回调。
  • 通过使用 ContinueWhenAll 和 ContinueWhenAny 方法或者 WaitAll 方法或 WaitAny 方法,协调多个为了响应 Begin_ 方法而执行的操作。
  • 在同一 Task 对象中封装异步 I/O 绑定和计算绑定操作。
  • 监视 Task 对象的状态。
  • 使用 TaskCompletionSource<TResult> 将操作的状态封送到 Task 对象。

 

下面介绍一下TPL是如何与传统的.NET 异步相互结合在一起使用的。

 

在任务中包装 APM 操作

 

System.Threading.Tasks.TaskFactory 和 System.Threading.Tasks.TaskFactory<TResult> 类都提供了 FromAsync 方法的一些重载,这些重载使您可以在一个 Task 实例或 Task<TResult> 实例中封装一个 APM Begin/End 方法对。各种重载可容纳任何具有零到三个输入参数的 Begin/End 方法对。 对于具有返回值的 End 方法(在 Visual Basic 中为 Function)的对,在创建 Task<TResult> 的 TaskFactory<TResult> 中使用这些方法。对于返回 void 的 End 方法(在 Visual Basic 中为 Sub),在创建 Task 的 TaskFactory 中使用这些方法。 对于 Begin 方法有三个以上的参数或包含 ref 或 out 参数的少数情况,提供了仅封装 End 方法的附加 FromAsync 重载。 下面的代码示例演示与 FileStream.BeginRead 和 FileStream.EndRead 方法匹配的 FromAsync 重载的签名。此重载采用三个输入参数,如下所示。

 

[c-sharp] view plain copy  
  1. public Task<TResult> FromAsync<TArg1, TArg2, TArg3>(  
  2.     Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod, //BeginRead  
  3.      Func<IAsyncResult, TResult> endMethod, //EndRead  
  4.      TArg1 arg1, // the byte[] buffer  
  5.      TArg2 arg2, // the offset in arg1 at which to start writing data  
  6.      TArg3 arg3, // the maximum number of bytes to read  
  7.      object state // optional state information  
  8.     )   
 

 

 

第一个参数是与 FileStream.BeginRead 方法的签名匹配的 Func<T1, T2, T3, T4, T5, TResult> 委托。第二个参数是采用 IAsyncResult 并返回 TResult 的 Func<T, TResult> 委托。由于 EndRead 返回一个整数,因此编译器将 TResult 的类型推断为 Int32,将任务的类型推断为 Task<Int32>。最后四个参数等同于 FileStream.BeginRead 方法中的那些参数:

  • 在其中存储文件数据的缓冲区。
  • 缓冲区中开始写入数据的偏移量。
  • 要从文件中读取的最大数据量。
  • 用于存储要传递给回调的用户定义状态数据的可选对象。

 

将 ContinueWith 用于回调功能

 

 

如果需要访问文件中的数据,而不只是访问字节数,则使用 FromAsync 方法是不够的。而应当使用 Task<String>,其 Result 属性包含文件数据。可以通过向原始任务添加延续任务来执行此操作。延续任务执行 AsyncCallback 委托通常执行的工作。当前面的任务完成并且数据缓冲区已填充时,会调用它。(FileStream 对象应在返回之前关闭)。

 

下面的示例演示如何返回 Task<String>,它封装了 FileStream 类的 BeginRead/EndRead 对。

 

[c-sharp] view plain copy  
  1. const int MAX_FILE_SIZE = 14000000;  
  2. public static Task<string> GetFileStringAsync(string path)  
  3. {  
  4.     FileInfo fi = new FileInfo(path);  
  5.     byte[] data = null;  
  6.     data = new byte[fi.Length];  
  7.     FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, data.Length, true);  
  8.     //Task<int> returns the number of bytes read  
  9.     Task<int> task = Task<int>.Factory.FromAsync(  
  10.             fs.BeginRead, fs.EndRead, data, 0, data.Length, null);  
  11.     // It is possible to do other work here while waiting  
  12.     // for the antecedent task to complete.  
  13.     // ...  
  14.     // Add the continuation, which returns a Task<string>.   
  15.     return task.ContinueWith((antecedent) =>  
  16.     {  
  17.         fs.Close();  
  18.         // Result = "number of bytes read" (if we need it.)  
  19.         if (antecedent.Result < 100)  
  20.         {  
  21.             return "Data is too small to bother with.";  
  22.         }  
  23.         else  
  24.         {  
  25.             // If we did not receive the entire file, the end of the  
  26.             // data buffer will contain garbage.  
  27.             if (antecedent.Result < data.Length)  
  28.                 Array.Resize(ref data, antecedent.Result);  
  29.             // Will be returned in the Result property of the Task<string>  
  30.             // at some future point after the asynchronous file I/O operation completes.  
  31.             return new UTF8Encoding().GetString(data);  
  32.         }  
  33.     });  
  34. }  
 

 

然后可以按如下所示调用该方法。

 

[c-sharp] view plain copy  
  1. Task<string> t = GetFileStringAsync(path);            
  2. // Do some other work:  
  3. // ...  
  4. try  
  5. {  
  6.      Console.WriteLine(t.Result.Substring(0, 500));  
  7. }  
  8. catch (AggregateException ae)  
  9. {  
  10.     Console.WriteLine(ae.InnerException.Message);  
  11. }              
 

 

 

Task.Delay() 和 Thread.Sleep() 区别

1、Thread.Sleep 是同步延迟,Task.Delay异步延迟。

2、Thread.Sleep 会阻塞线程,Task.Delay不会。

3、Thread.Sleep不能取消,Task.Delay可以。

4. Task.Delay() 比 Thread.Sleep() 消耗更多的资源,但是Task.Delay()可用于为方法返回Task类型;或者根据CancellationToken取消标记动态取消等待

5. Task.Delay() 实质创建一个运行给定时间的任务, Thread.Sleep() 使当前线程休眠给定时间。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: