威尼斯人线上娱乐

听说职责的异步方式,四线程编制程序体系

30 3月 , 2019  

目录

 IO操作的MDA(Direct memory
access)情势:直接待上访问内部存款和储蓄器,是一种不经过CPU而直接举办内部存款和储蓄器数据存款和储蓄的数据调换形式,大致能够不损耗CPU的能源;
 CLWrangler所提供的异步编制程序模型正是充足利用硬件的DMA效用来释放CPU的压力;使用线程池实行田管,异步将工作移交给线程池中的有个别工作线程来完结,直到异步实现,异步才会经过回调的章程布告线程池,让CL凯雷德响应异步实现;

正文内容

  • 概述
  • 编排异步方法
  • 异步程序中的控制流
  • API 异步方法
  • 线程
  • 异步和等待
  • 归来类型和参数
  • 参考资料

复习:

  • 1.1
    简介
  • 1.2
    创造任务
  • 1.3
    使用职分执行基本的操作
  • 1.4
    组合任务
  • 1.5
    将APM情势转换为天职
  • 1.6
    将EAP格局转换为天职
  • 1.7
    实现废除选项
  • 1.8
    处理义务中的万分
  • 1.9
    相互运维任务
  • 1.10
    使用TaskScheduler配置职责执行
  • 参考书籍
  • 作者水平有限,要是不当欢迎各位批评指正!

它是出现的一种方式,它使用 future
情势或回调(callback)机制,避防止产生不须求的线程。贰个 future(或
promise)类型代表有些就要完毕的操作。在 .NET 中,新版 future 类型有Task
和Task<TResult>。 

下载 Demo

其三章内容中我们关系了二种异步编制程序模型,这里大约复习一下,分别如下


异步编程方式——动用委托和线程池完毕的格局

APM 异步编制程序模型,Asynchronous Programming Model            C#1.0

EAP 基于事件的异步编制程序格局,伊夫nt-based Asynchronous Pattern  C#2.0

TAP 基于职务的异步编制程序情势,Task-based Asynchronous Pattern    C#4.0

Async\await简化异步编制程序;职务并行库,Task Parallel Library     C#5

下载 Demo TPL 与 APM 和 EAP 结合(APM 和 EAP 那多少个标准异步格局已经不可能适应多核时代,但前边用那二种方法写的代码咋做?——把它们改造一下,跟 TPL 结合)

1.APM(异步编制程序形式):形如Beginxxx,Endxxx。

本体系首页链接:[C#多线程编程种类(一)-
简介 ]

APM

         使用IAsyncResult设计形式的异步操作是透过名为 BeginXXX 和 EndXXX
的四个方法来落到实处,那八个方法分别指起头和终结异步操作。该格局允许用更少的CPU能源(线程)去做更加多的操作,.NET
Framework很多类也落实了该形式,同时大家也得以自定义类来落到实处该方式(相当于在自定义的类中贯彻重临类型为IAsyncResult接口的BeginXXX方法和承受IAsyncResult包容类型作为唯一参数的EndXXX方法),别的事委员会托项目也定义了BeginInvoke和EndInvoke方法。例如,FileStream类提供BeginRead和EndRead方法来从文件异步读取字节。那多少个方法达成了
Read 方法的异步版本。

调用 BeginXXX
后,应用程序能够继续在调用线程上执行命令,同时异步操作在另多少个线程上实施(假使有重回值还应调用
EndXXX停止异步操作,并向该格局传递BeginXXX
方法重临的IAsyncResult对象,赢得操作的返回值)。

 威尼斯人线上娱乐 1

CompletedSynchronously属性值侧重与提示音讯,而非操作

走访异步操作的结果,APM提供了多样格局:

1.在调用BeginXXX方法的线程上调用EndXXX方法来获取异步操作的结果;然而这种格局会阻塞调用线程,在通晓操作达成未来调用线程才能一而再运维。

2.循环查询IAsyncResult的IsComplete属性,操作达成后再调用EndXXX方法来博取操作重返的结果。

3.IAsyncResult的AsyncWaitHandle属性完结越发灵敏的守候逻辑,调用该属性WaitOne()方法来使三个线程阻塞并伺机操作达成;再调用EndXXX方法来取得操作的结果。WaitHandle.WaitOne()能够内定最长的守候时间,如超时再次回到false;

4.
在调用BeginXXX方法时提供AsyncCallback委托的实例作为参数,在异步操作完结后委托会自动调用(AsyncCallback对象)钦定的措施。(首要选拔办法)AsyncCallback委托仅能够调用符合一定形式的办法(只有3个参数IAsyncResult,且没有重返值);

威尼斯人线上娱乐 2威尼斯人线上娱乐 3

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Messaging;

namespace AsyncCallbackDelegate
{
    public delegate int BinaryOp(int x, int y);
    class Program
    {
        private static bool isDone = false;
        static void Main(string[] args)
        {
            Console.WriteLine("*****  AsyncCallbackDelegate Example *****");
            Console.WriteLine("Main() invoked on thread {0}.",
              Thread.CurrentThread.ManagedThreadId);
            BinaryOp b = new BinaryOp(Add);
            IAsyncResult iftAR = b.BeginInvoke(10, 10,
              new AsyncCallback(AddComplete),
              "Main() thanks you for adding these numbers.");//传入数据
            // Assume other work is performed here...
            while (!isDone)
            {
                Thread.Sleep(1000);
                Console.WriteLine("Working....");
            }
            Console.ReadLine();
        }

        #region Target for AsyncCallback delegate
        // Don't forget to add a 'using' directive for 
        // System.Runtime.Remoting.Messaging!
        static void AddComplete(IAsyncResult itfAR)
        {
            Console.WriteLine("AddComplete() invoked on thread {0}.",
              Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Your addition is complete");

            // Now get the result.
            //AsyncCallback委托的目标无法调用其他方法中创建的委托
            //IAsyncResult itfAR 实际上是System.Runtime.Remoting.Messaging命名空间AsyncResult类的一个实例
            AsyncResult ar = (AsyncResult)itfAR;
            //AsyncDelegate静态属性返回原始异步委托引用
            BinaryOp b = (BinaryOp)ar.AsyncDelegate;
            Console.WriteLine("10 + 10 is {0}.", b.EndInvoke(itfAR));

            // Retrieve the informational object and cast it to string.
            //AsyncState属性获取 BeginInvoke第四个参数传入的值
            string msg = (string)itfAR.AsyncState;
            Console.WriteLine(msg);
            isDone = true;
        }

        #endregion

        #region Target for BinaryOp delegate
        static int Add(int x, int y)
        {
            Console.WriteLine("Add() invoked on thread {0}.",
              Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(5000);
            return x + y;
        }
        #endregion
    }
}

AsyncCallback

不行捕获

在一起实施的方式里面普通处理万分的方法是将大概抛出卓殊的代码放到try…catch…finally里面,之所以可以捕获到,是因为暴发格外的代码与调用的代码位于同一个线程。当调用三个异步方法爆发极度时,CL索罗德会捕获并且在EndXXX方法时再也将丰硕抛出抛出,所以异步调用中的相当在EndXXX方法出捕获就行了。

class ApmExceptionHandling 
{
   public static void Go() 
  {
      WebRequest webRequest = WebRequest.Create("http://0.0.0.0/");
      webRequest.BeginGetResponse(ProcessWebResponse, webRequest);
      Console.ReadLine();
   }
   private static void ProcessWebResponse(IAsyncResult result) {
      WebRequest webRequest = (WebRequest)result.AsyncState;

      WebResponse webResponse = null;
      try {
         webResponse = webRequest.EndGetResponse(result);
         Console.WriteLine("Content length: " + webResponse.ContentLength);
      }
      catch (WebException we) {
         Console.WriteLine(we.GetType() + ": " + we.Message);
      }
      finally {
         if (webResponse != null) webResponse.Close();
      }
   }
}

APM WinForm UI线程回调

鉴于AsyncCallback委托回调是从ThreadPool中的线程执行的,因而对此Winform,若是回调供给操作UI控件,就需求重临到UI线程去,常用的三个格局:

1. 
Control类完成了ISynchronizeInvoke接口,提供了Invoke和BeginInvoke方法来支持别的线程更新GUI界面控件的机制(将回调方法投递到开创该控件的线程中实践)。

 威尼斯人线上娱乐 4

Control类的 Invoke,BeginInvoke 内部贯彻如下:

a)
Invoke(同步调用)先判断控件制造线程与近日线程是不是相同,相同则平昔调用委托方法;不然使用Win32API的PostMessage异步执行,可是Invoke内部会调用IAsyncResult.AsyncWaitHandle等待执行到位。

b) BeginInvoke(异步调用)使用Win32API的PostMessage 异步执行,并且重返IAsyncResult 对象。

利用方法:回调方法中对控件检查和测试InvokeRequired值,if
true,在该回调中封送三回委托,调用控件的Invoke/ BeginInvoke方法;

 威尼斯人线上娱乐 5

2.GUI(WinForm/WPF)应用程序引入了一个线程处理模型:创制窗口的线程是绝无仅有能对足够窗口进行更新的线程;在GUI线程中,平日索要转移异步操作,使GUI线程不打断并甘休响应用户输入。然则,异步操作达成时,由于是用2个线程池线程完结的,而线程池线程不可能更新UI控件。为缓解那一个难题,FCL定义四个System.Threading.SynchronizationContext(线程同步上下文)的基类,其派生对象承担将1个应用程序模型连接到它的线程处理模型。

GUI线程都有2个和它事关的SynchronizationContext派生对象,使用其静态Current属性获取:SynchronizationContext
sc = SynchronizationContext.Current;
将此指标传给其余线程,当四个线程池线程供给让GUI线程更新UI时,调用该指标的sc.Post方法,向Post传递一个匹配SendOrPostCallback委托签名的回调方法(一般是更新UI的操作方法,由GUI线程去实践),以及三个要传给回调方法的实参。

SynchronizationContext
的Post方法和Send方法的分裂:(分别对应于异步/同步调用)

Post方法将回调方法送人GUI线程的队列,允许程序池线程立即返回,不进行阻塞;Post方法内部调用了BeginInvoke方法;

Send方法也将回调方法送人GUI线程的队列,但随后就会阻塞线程池线程,直到GUI线程完成对回调方法的调用。阻塞线程池线程极有可能造成线程池创建一个新的线程,避免调用该方法;Send方法内部调用了Invoke方法; 

对winform来说是
System.Windows.Forms.WindowsFormsSynchronizationContext是其子类.

Winform窗口冒出后,UI线程
SynchronizationContext.Current会被绑定赋值,只有UI线程的Current不为null。

Public class SendOrPostUI {
   public static void Go() {
      System.Windows.Forms.Application.Run(new MyWindowsForm());
   }
   private static AsyncCallback SyncContextCallback(AsyncCallback callback) {
      // Capture the calling thread's SynchronizationContext-derived object
      SynchronizationContext sc = SynchronizationContext.Current;
      // If there is no SC, just return what was passed in
      if (sc == null) return callback;
      // Return a delegate that, when invoked, posts to the captured SC a method that 
      // calls the original AsyncCallback passing it the IAsyncResult argument
      return asyncResult => sc.Post(result => callback((IAsyncResult)result), asyncResult);
   }
   private sealed class MyWindowsForm : System.Windows.Forms.Form {
      public MyWindowsForm() {
         Text = "Click in the window to start a Web request";
         Width = 400; Height = 100;
      }
      protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e) {
         // The GUI thread initiates the asynchronous Web request 
         Text = "Web request initiated";
         var webRequest = WebRequest.Create("http://Wintellect.com/");
         webRequest.BeginGetResponse(SyncContextCallback(ProcessWebResponse), webRequest);
         base.OnMouseClick(e);
      }
      private void ProcessWebResponse(IAsyncResult result) {
         // If we get here, this must be the GUI thread, it's OK to update the UI
         var webRequest = (WebRequest)result.AsyncState;
         using (var webResponse = webRequest.EndGetResponse(result)) {
            Text = "Content length: " + webResponse.ContentLength;
         }
      }
   }
}

正如三种格局其实差不太多,三个是回调内再一次卷入,八个是包装原来的回调。可是SynchronizationContext业务层与UI分离来讲的话是比较好;

概述


异步对恐怕起阻止成效的移位(例如,应用程序访问 Web 时)至关心重视要。 对 Web
能源的访问有时非常慢或会推迟。
就算此类活动在同步进度中受阻,则全体应用程序必须等待。在异步进程中,应用程序可继续执行不重视Web 财富的别样干活,直至潜在阻止任务成功。

下表是应用异步编制程序能增高响应能力的天下第②气象。从 .NET Framework 4.5 和
Windows 运维时中列出的 API 包蕴匡助异步编程的点子。

应用程序区域

包含异步方法的受支持的 API

Web 访问

HttpClient ,SyndicationClient

使用文件

StorageFile、StreamWriter、StreamReader、XmlReader

使用图像

MediaCapture、BitmapEncoder、BitmapDecoder

WCF 编程

同步和异步操作

出于具备与用户界面相关的位移一般共享三个线程,因而,异步对走访 UI
线程的应用程序来说越发重大。要是别的进程在一块儿应用程序中受阻,则持有进度都将受阻。
你的应用程序结束响应,由此,你大概在其等待进程中以为它曾经失利。

应用异步方法时,应用程序将持续响应 UI。
例如,你能够调整窗口的轻重或最小化窗口;假使您不期望等待应用程序停止,则能够将其关闭。

能够选用三种方法来完成 TAP:即手动使用 C#
编写翻译器,或将编写翻译器和手动方法结合使用。使用 TAP 形式来促成总计密集型和
I/O 密集型异步操作。

  • 选择编写翻译器。在 Visual Studio 二零一一 和 .NET Framework 4.5中,任何具有 async 关键字的主意都被看作是一种异步方法,并且 C#
    会执行要求的更换,以通过 TAP 来异步达成该办法。 异步方法应再次来到System.Threading.Tasks.Task 或
    System.Threading.Tasks.Task<TResult> 对象。
  • 手动生成 TAP 方法。也能够手动实现 TAP
    格局,以更好地控制落到实处。编译器依赖从 System.Threading.Tasks
    命名空间公开的公家外围应用和 System.Runtime.CompilerServices
    命名空间中帮助的门类。 如要本人达成 TAP,你必要成立2个TaskCompletionSource<TResult>
    对象、执行异步操作,并在操作完毕时,调用
    SetResult、SetException、SetCanceled
    方法,或调用那几个措施之一的Try版本。 手动完成 TAP
    方法时,需在所代表的异步操作完毕时成功生成的天职。 例如:
  • 掺杂方法。你恐怕发现手动实现 TAP
    情势、但将贯彻主题逻辑委托给编写翻译器的那种办法很有用。
    例如,当您想要验证编写翻译器生成的异步方法之外的实参时,恐怕须求动用那种混合方法,以便万分能够转义到该办法的第②手调用方而不是透过
    System.Threading.Tasks.Task 对象被公开:

本文主要表达“使用编写翻译器”方法。

2.EAP(基于事件的异步编制程序格局):那一个我们在.net中采纳到了BackgroudWorker组件,使用办法是通过事件绑定处理的不二法门。


EAP

EAP是为了更便于处理UI的更新推出的模式,主要优点:它同Visual Studio UI设计器进行了很好的集成,可将大多数实现了EAP的类拖放到设计平面(design surface)上,双击控件对应的XXXCompleted事件名,会自动生成事件的回调方法,并将方法同事件自身联系起来。EAP保证事件在应用程序的GUI线程上引发,允许事件回调方法中的代码更新UI控件;

EAP另一重要功能:支持EAP的类自动将应用程序模型映射到它的线程处理模型;EAP类在内部使用SynchronizationContext类。有的EAP类提供了取消、进度报告功能。

   FCL中唯有1多个连串完毕了EAP情势,一般有3个XXXAsync方法和3个对应的XXXCompleted事件,以及这一个点子的一道版本:

*       System.Object的派生类型:*

*                  System.Activies.WorkflowInvoke  *

*威尼斯人线上娱乐,                 
System.Deployment.Application.ApplicationDeployment*

*                  System.Deployment.Application.InPlaceHosingManager*

*                  System.Net.Mail.SmtpClient*

*                  System.Net.PeerToPeer.PeerNameResolver*

*                  System.Net.PeerToPeer.Collaboration.ContactManager*

*                  System.Net.PeerToPeer.Collaboration.Peer*

*                  System.Net.PeerToPeer.Collaboration.PeerContact*

*                  System.Net.PeerToPeer.Collaboration.PeerNearMe*

*                 
System.ServiceModel.Activities.WorkflowControlClient*

*                  System.ServiceModel.Discovery.AnnoucementClient*

*                  System.ServiceModel.Discovery.DiscoveryClient*

*      System.ComponentModel.Component的派生类型:听说职责的异步方式,四线程编制程序体系。*

                 
System.ComponentModel.BackgroundWorker

                 
System.Media.SoundPlay

                 
System.Net.WebClient

                 
System.Net.NetworkInformation.Ping

                 
System.Windows.Forms.PictureBox(继承于Control类,Control类派生于Component类)

private sealed class MyForm : System.Windows.Forms.Form {
    protected override void OnClick(EventArgs e) {
      // The System.Net.WebClient class supports the Event-based Asynchronous Pattern
      WebClient wc = new WebClient();
      // When a string completes downloading, the WebClient object raises the 
      // DownloadStringCompleted event which will invoke our ProcessString method         
      wc.DownloadStringCompleted += ProcessString;
      // Start the asynchronous operation (this is like calling a BeginXxx method)
      wc.DownloadStringAsync(new Uri("http://Wintellect.com"));
      base.OnClick(e);
    }
    // This method is guaranteed to be called via the GUI thread
    private void ProcessString(Object sender, DownloadStringCompletedEventArgs e) {
      // If an error occurred, display it; else display the downloaded string
      System.Windows.Forms.MessageBox.Show((e.Error != null) ? e.Error.Message : e.Result);
      }
   }

BackgroundWorker:唯有该类型用于可用来实施异步的总括范围的干活;提供三个事件:

DoWork:向这几个事件登记的法子应该包罗总括范围的代码。那些事件由3个线程池线程调用RunWorkerAsync(五个重载方法,带参的点子是向DoWork登记的主意的DoWork伊夫ntArgs参数对象的Argument属性传值,只幸亏注册的法门中(如e.Argument)获取,Result属性必须设置成总括范围的操作希望回到的值)时引发;

ProgressChanged:向这么些事件登记的办法应该包涵使用进程新闻来更新UI的代码。那一个事件一而再在GUI线程上掀起。DoWork登记的主意必须定期调用BackgroundWorker的ReportProgress方法来吸引ProgressChanged事件;

RunWorkerCompleted:向那个事件登记的方法应该包涵使用总结范围操作的结果对UI实行更新的代码。那个事件一连在GUI线程上掀起。Result获取表示异步操作的结果;

国有性质:CancellationPending(标识是不是已呼吁撤废后台操作)、IsBusy(标识是还是不是正在运营异步操作)、WorkReportsProgress(获取/设置是或不是报告进程更新)、WorkerSupportsCancellation(获取/设置是还是不是协助异步撤废)

公家措施:CancelAsync(请求裁撤挂起的后台操作)、ReportProgress、RunWorkerAsync

异常

这几个不会抛出。在XXXCompleted事件处理方法中,必须询问AsyncCompleted伊芙ntArgs的Exception属性,看它是否null。假若不是null,就必须使用if语句判断Exception派生对象的品种,而不是运用catch块。

编排异步方法


C# 中 asyncawait
关键字是异步编制程序的中坚。通过那五个至关心珍视要字就足以轻松创造异步方法,大概与创立同步方法同样。如下所示的
WPF 程序,布局文件上有个按钮和文本框:

private async void StartButton_Click(object sender, RoutedEventArgs e)

{

    // Call and await separately.

    //Task<int> getLengthTask = AccessTheWebAsync();

    //// You can do independent work here.

    //int contentLength = await getLengthTask;

 

    int contentLength = await AccessTheWebAsync();

 

    resultsTextBox.Text +=

        String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength);

}

 

 

// Three things to note in the signature:

//  - The method has an async modifier. 

//  - The return type is Task or Task<T>. (See "Return Types" section.)

//    Here, it is Task<int> because the return statement returns an integer.

//  - The method name ends in "Async."

async Task<int> AccessTheWebAsync()

{ 

    // You need to add a reference to System.Net.Http to declare client.

    HttpClient client = new HttpClient();

 

    // GetStringAsync returns a Task<string>. That means that when you await the

    // task you'll get a string (urlContents).

    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

 

    // You can do work here that doesn't rely on the string from GetStringAsync.

    DoIndependentWork();

 

    // The await operator suspends AccessTheWebAsync.

    //  - AccessTheWebAsync can't continue until getStringTask is complete.

    //  - Meanwhile, control returns to the caller of AccessTheWebAsync.

    //  - Control resumes here when getStringTask is complete. 

    //  - The await operator then retrieves the string result from getStringTask.

    string urlContents = await getStringTask;

 

    // The return statement specifies an integer result.

    // Any methods that are awaiting AccessTheWebAsync retrieve the length value.

    return urlContents.Length;

}

 

 

void DoIndependentWork()

{

    resultsTextBox.Text += "Working . . . . . . .\r\n";

}

执行结果:

Working . . . . . . .

 

Length of the downloaded string: 41609.

说明:

1,当程序访问互连网时,无论你什么样拖拽、最大化最小化、怎样点击,UI
都不会错过响应;

2,“async Task<int>
AccessTheWebAsync()”方法签名,有三点要求留意:1)有 async
修饰符;2)重返类型是 TaskTask<int>。该措施是
Task<int>,因为它回到的是链接内容的尺寸;3)方法名以 Async
结尾;

3,“string urlContents = await
getStringTask;”语句,有四点供给留意:1)AccessTheWebAsync 方法直到
getStringTask 实现才能接二连三;2)同时,控制流再次回到到
AccessTheWebAsync 的调用者;3)getStringTask
实现后,控制流才会卷土重来;4)之后,await 操作符从 getStringTask
检索结果。

下边计算让几个示范成为异步方法的表征:

  • 办法签名包罗1个 async 修饰符。
  • 遵照预订,异步方法的称呼以“Async”后缀结尾。
  • 回到类型为下列项目之一:
    • 假设你的不二法门有 TResult 类型的回来语句,则为
      Task<TResult>。
    • 借使您的法子没有重返语句,则为 Task。
    • 万一您编写的是异步事件处理程序,则为 Void(Visual Basic 中为
      Sub)。
  • 措施一般包括至少五个 await
    表明式,该表明式标记三个点,在该点上,直到等待的异步操作达成章程才能延续。同时,将艺术挂起,并且控件再次来到到方式的调用方。

在异步方法中,可选择提供的关键字和品种来提醒需求达成的操作,且编写翻译器会完成其他操作,其中蕴含不断跟踪控件以挂起方法重回等待点时发生的景况。
一些符合规律流程(例如,循环和那个处理)在古板异步代码中拍卖起来恐怕很拮据。
在异步方法中,成分的编写制定频率与一块化解方案相同且此难题得到缓解。

3.TPL(基于任务的异步编制程序方式):那几个就会用到职分并行库。

1.1 简介

在前面包车型地铁多少个章节中,就线程的利用和八线程相关的始末展开了介绍。因为线程涉及到异步、同步、卓殊传递等题材,所以在项目中央银行使八线程的代价是相比高昂的,须求编写制定大批量的代码来达成科学和健壮性。

为了解决那样局地的题目,在.Net Framework 4.0中引入了八个关于一步操作的API。它称为任务并行库(Task
Parallel
Library)
。然后在.Net Framwork 4.5中对它进行了轻微的句酌字斟,本文的案例都以用前卫版本的TPL库,而且大家仍可以使用C#
5.0的新特点await/async来简化TAP编制程序,当然那是后来才介绍的。

TPL内部使用了线程池,可是效能更高。在把线程归还回线程池在此之前,它会在同一线程中逐一执行稍微Task,那样防止了某些小职务上下文切换浪费时间片的标题。

职务是指标,当中封装了以异步格局进行的工作,可是委托也是包装了代码的目的。职务和信托的分别在于,委托是同步的,而任务是异步的。

在本章中,大家将会商量如何利用TPL库来开展职责之间的结缘同步,如何将残留的APM和EAP形式转换为TPL形式等等。

TAP

.NET4.0
中引入了新的异步编制程序模型“基于任务的异步编制程序模型(TAP)”,并且推荐我们在开发新的三十二线程应用程序中首要选用TAP,在.NET4.5中更为对TPL库举行了大批量的优化与改进(async和await)。那今后小编先介绍下TAP具有怎么样优势:

  1. 义务调度器(TaskScheduler)依赖于底层的线程池引擎,可自定义八个TaskScheduler更改调度算法,同时不改变代码或编制程序模型。通过一些队列的职务内联化(task
    inlining)和做事窃取(work-stealing)机制而发起了大气任务,Task能够为大家升高程序质量。
  2. 能够应用PreferFairness标志,获取与ThreadPool.QueueUserWorkItem可能多个委托的BeginInvoke相同的线程池行为。

        3.
 轻松完结任务等待、职分打消、一而再职务、极度处理(System.AggregateException)、GUI线程操作。

       4.  在任务运维后,能够每天以任务一连的样式登记回调。

       5.  丰硕利用现有的线程,防止成立不须要的额外线程。

       6.  结合C#5.0引入async和await关键字轻松完成“异步方法”。

APM转换为TAP:

使用TaskFactory的FromAsync方法,传递七个实参:BeginXxx方法、EndXxx方法、Object状态、可选的TaskCreationOptions值,再次来到对一个Task对象的引用;

private static void ConvertingApmToTask() {
      // Instead of this:
      WebRequest webRequest = WebRequest.Create("http://Wintellect.com/");
      webRequest.BeginGetResponse(result => {
         WebResponse webResponse = null;
         try {
            webResponse = webRequest.EndGetResponse(result);
            Console.WriteLine("Content length: " + webResponse.ContentLength);
         }
         catch (WebException we) {
            Console.WriteLine("Failed: " + we.GetBaseException().Message);
         }
         finally {
            if (webResponse != null) webResponse.Close();
         }
      }, null);
      Console.ReadLine();  // for testing purposes
      // Make a Task from an async operation that FromAsync starts
      webRequest = WebRequest.Create("http://Wintellect.com/");
      var t1 = Task.Factory.FromAsync<WebResponse>(webRequest.BeginGetResponse, webRequest.EndGetResponse, null, TaskCreationOptions.None);
      var t2 = t1.ContinueWith(task => {
         WebResponse webResponse = null;
         try {
            webResponse = task.Result;
            Console.WriteLine("Content length: " + webResponse.ContentLength);
         }
         catch (AggregateException ae) {
            if (ae.GetBaseException() is WebException)
               Console.WriteLine("Failed: " + ae.GetBaseException().Message);
            else throw;
         }
         finally { if (webResponse != null) webResponse.Close(); }
      });
      try {t2.Wait();  // for testing purposes only}
      catch (AggregateException) { }
   }

EAP转换成TAP:

动用System.Threading.Tasks.TaskCompletionSource类进行李包裹装;

威尼斯人线上娱乐 6

当协会2个TaskCompletionSource对象,也会变动三个Task,可因此其Task属性获取;当三个异步操作完毕时,它利用TaskCompletionSource对象来设置它因为何而形成,废除,未处理的不行可能它的结果。调用有些SetXxx方法,能够安装底层Task对象的图景。

private sealed class MyFormTask : System.Windows.Forms.Form {
      protected override void OnClick(EventArgs e) {
         // The System.Net.WebClient class supports the Event-based Asynchronous Pattern
         WebClient wc = new WebClient();
         // Create the TaskCompletionSource and its underlying Task object
         var tcs = new TaskCompletionSource<String>();
         // When a string completes downloading, the WebClient object raises the 
         // DownloadStringCompleted event which will invoke our ProcessString method
         wc.DownloadStringCompleted += (sender, ea) => {
            // This code always executes on the GUI thread; set the Task’s state
            if (ea.Cancelled) tcs.SetCanceled();
            else if (ea.Error != null) tcs.SetException(ea.Error);
            else tcs.SetResult(ea.Result);
         };
         // Have the Task continue with this Task that shows the result in a message box
// NOTE: The TaskContinuationOptions.ExecuteSynchronously flag is required to have this code
         // run on the GUI thread; without the flag, the code runs on a thread pool thread 
         tcs.Task.ContinueWith(t => {
            try { System.Windows.Forms.MessageBox.Show(t.Result);}
            catch (AggregateException ae) {
               System.Windows.Forms.MessageBox.Show(ae.GetBaseException().Message);
            }
         }, TaskContinuationOptions.ExecuteSynchronously);
         // Start the asynchronous operation (this is like calling a BeginXxx method)
         wc.DownloadStringAsync(new Uri("http://Wintellect.com"));
         base.OnClick(e);
      }
   }

实现了TAP的类:存在XxxTaskAsync的方法,
协助异步操作的吊销和进程的告诉的效率;

撤除:能够通过同盟式裁撤格局,向异步方法传入CancellationToken 参数,通过调用其ThrowIfCancellationRequested方法来定时检查操作是还是不是曾经撤除;

进程报告:能够由此IProgress<T>接口来落到实处速度报告的功用;

履新GUI:
TaskScheduler.FromCurrentSynchronizationContext()获取同步上下文职分调度器,将关乎该对象的装有义务都调度给GUI线程,使职务代码能学有所成更新UI;

private sealed class MyForm : System.Windows.Forms.Form {
        public MyForm() {
            Text = "Synchronization Context Task Scheduler Demo";
            Visible = true; Width = 400; Height = 100;
        }
         private static Int32 Sum(CancellationToken ct, Int32 n) {
        Int32 sum = 0;
        for (; n > 0; n--) {
            // The following line throws OperationCanceledException when Cancel 
            // is called on the CancellationTokenSource referred to by the token
            ct.ThrowIfCancellationRequested();
            //Thread.Sleep(0);   // Simulate taking a long time
            checked { sum += n; }
        }
        return sum;
       }
        private readonly TaskScheduler m_syncContextTaskScheduler =
           TaskScheduler.FromCurrentSynchronizationContext();
        private CancellationTokenSource m_cts;
        protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e) {
            if (m_cts != null) {    // An operation is in flight, cancel it
                m_cts.Cancel();
                m_cts = null;
            } else {                // An operation is not in flight, start it
                Text = "Operation running";
                m_cts = new CancellationTokenSource();
           // This task uses the default task scheduler and executes on a thread pool thread
                var t = new Task<Int32>(() => Sum(m_cts.Token, 20000), m_cts.Token);
                t.Start();
 // These tasks use the synchronization context task scheduler and execute on the GUI thread
                t.ContinueWith(task => Text = "Result: " + task.Result,
                   CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion,
                   m_syncContextTaskScheduler);
                t.ContinueWith(task => Text = "Operation canceled",
                   CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled,
                   m_syncContextTaskScheduler);
                t.ContinueWith(task => Text = "Operation faulted",
                   CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted,
                   m_syncContextTaskScheduler);
            }
            base.OnMouseClick(e);
        }
}

特别处理

在职分抛出的未处理相当都封装在System.AggregateException对象中。那几个目标会蕴藏在艺术再次回到的Task或Task<TResult>对象中,须求经过访问Wait()、Result、Exception成员才能观测到充裕。(所以,在拜访Result从前,应先观望IsCanceled和IsFaulted属性)

假定一向不访问Task的Wait()、Result、Exception成员,那么您将永久注意不到那么些很是的发出。为了救助你检查和测试到那些未处理的非常,能够向TaskScheduler对象的UnobservedTaskException事件注册回调函数。每当2个Task被垃圾回收时,假如存在1个一直不留神到的格外,CL奥迪Q5的收尾器线程会引发这几个事件。

可在事变回调函数中调用UnobservedTaskException伊夫ntArgs对象的SetObserved()
方法来提议已经处理好了充足,从而阻碍CLOdyssey终止线程。可是并不推荐这么做,宁愿终止进度也无须带着早已磨损的情状继续运转。

异步程序中的控制流


异步编制程序中最需弄清的是控制流是什么样从点子移动到艺术。

private async void StartButton_Click(object sender, RoutedEventArgs e)

       {

           // Call and await separately.

           //Task<int> getLengthTask = AccessTheWebAsync();

           //// You can do independent work here.

           //int contentLength = await getLengthTask;

           resultsTextBox.Text += "1:  Entering startButton_Click.\r\n" +

               "           Calling AccessTheWebAsync.\r\n";

 

           int contentLength = await AccessTheWebAsync();

 

           resultsTextBox.Text +=

               String.Format("\r\n6:   Length of the downloaded string: {0}.\r\n", contentLength);

       }

 

       async Task<int> AccessTheWebAsync()

       {

           resultsTextBox.Text += "\r\n2:  Entering AccessTheWebAsync.";

 

           HttpClient client = new HttpClient();

 

           resultsTextBox.Text += "\r\n        Calling HttpClient.GetStringAsync.\r\n";

 

           Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

 

           DoIndependentWork();

 

           resultsTextBox.Text += "\r\n4:  Back in startButton_Click.\r\n" +

               "       Task getStringTask is started.\r\n";

           string urlContents = await getStringTask;

 

           resultsTextBox.Text += "\r\n5:  Back in AccessTheWebAsync." +

               "\r\n       Task getStringTask is complete." +

               "\r\n       Processing the return statement." +

               "\r\n       Exiting from AccessTheWebAsync.\r\n";

 

           return urlContents.Length;

       }

 

 

       void DoIndependentWork()

       {

           resultsTextBox.Text += "\r\n3:  Entering DoIndependentWork.\r\n";

 

           resultsTextBox.Text += "\r\n        Working . . . . . . .\r\n";

       }

运作结果:

1:  Entering startButton_Click.

           Calling AccessTheWebAsync.

 

2:  Entering AccessTheWebAsync.

        Calling HttpClient.GetStringAsync.

 

3:  Entering DoIndependentWork.

 

        Working . . . . . . .

 

4:  Back in startButton_Click.

       Task getStringTask is started.

 

5:  Back in AccessTheWebAsync.

       Task getStringTask is complete.

       Processing the return statement.

       Exiting from AccessTheWebAsync.

 

6:   Length of the downloaded string: 41609.

再稍加复杂点:

private async void startButton_Click(object sender, RoutedEventArgs e)

       {

           // The display lines in the example lead you through the control shifts.

           resultsTextBox.Text += "ONE:   Entering startButton_Click.\r\n" +

               "           Calling AccessTheWebAsync.\r\n";

 

           Task<int> getLengthTask = AccessTheWebAsync();

 

           resultsTextBox.Text += "\r\nFOUR:  Back in startButton_Click.\r\n" +

               "           Task getLengthTask is started.\r\n" +

               "           About to await getLengthTask -- no caller to return to.\r\n";

 

           int contentLength = await getLengthTask;

 

           resultsTextBox.Text += "\r\nSIX:   Back in startButton_Click.\r\n" +

               "           Task getLengthTask is finished.\r\n" +

               "           Result from AccessTheWebAsync is stored in contentLength.\r\n" +

               "           About to display contentLength and exit.\r\n";

 

           resultsTextBox.Text +=

               String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength);

       }

 

       async Task<int> AccessTheWebAsync()

       {

           resultsTextBox.Text += "\r\nTWO:   Entering AccessTheWebAsync.";

 

           // Declare an HttpClient object and increase the buffer size. The default

           // buffer size is 65,536.

           HttpClient client =

               new HttpClient() { MaxResponseContentBufferSize = 1000000 };

 

           resultsTextBox.Text += "\r\n           Calling HttpClient.GetStringAsync.\r\n";

 

           // GetStringAsync returns a Task<string>. 

           Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

 

           resultsTextBox.Text += "\r\nTHREE: Back in AccessTheWebAsync.\r\n" +

               "           Task getStringTask is started.";

 

           // AccessTheWebAsync can continue to work until getStringTask is awaited.

 

           resultsTextBox.Text +=

               "\r\n           About to await getStringTask and return a Task<int> to startButton_Click.\r\n";

 

           // Retrieve the website contents when task is complete.

           string urlContents = await getStringTask;

 

           resultsTextBox.Text += "\r\nFIVE:  Back in AccessTheWebAsync." +

               "\r\n           Task getStringTask is complete." +

               "\r\n           Processing the return statement." +

               "\r\n           Exiting from AccessTheWebAsync.\r\n";

 

           return urlContents.Length;

       }

运作结果:

ONE:   Entering startButton_Click.

           Calling AccessTheWebAsync.

 

TWO:   Entering AccessTheWebAsync.

           Calling HttpClient.GetStringAsync.

 

THREE: Back in AccessTheWebAsync.

           Task getStringTask is started.

           About to await getStringTask and return a Task<;int> to startButton_Click.

 

FOUR:  Back in startButton_Click.

           Task getLengthTask is started.

           About to await getLengthTask -- no caller to return to.

 

FIVE:  Back in AccessTheWebAsync.

           Task getStringTask is complete.

           Processing the return statement.

           Exiting from AccessTheWebAsync.

 

SIX:   Back in startButton_Click.

           Task getLengthTask is finished.

           Result from AccessTheWebAsync is stored in contentLength.

           About to display contentLength and exit.

 

Length of the downloaded string: 41635.

 

1.2 创制职责

在本节中,首即便出现说法了什么创立2个职务。其首要行使了System.Threading.Tasks命名空间下的Task类。该类能够被实例化并且提供了一组静态方法,能够方便急迅的创建职分。

在底下实例代码中,分别延时了三种普遍的职责创设格局,并且创建职分是足以内定任务创建的选项,从而达到最优的创办格局。

TaskCreationOptions中总共有八个枚举,枚举是能够使用|运算符组合定义的。其枚举如下表所示。

成员名称 说明
AttachedToParent 指定将任务附加到任务层次结构中的某个父级。 默认情况下,子任务(即由外部任务创建的内部任务)将独立于其父任务执行。 可以使用 TaskContinuationOptions.AttachedToParent 选项以便将父任务和子任务同步。请注意,如果使用 DenyChildAttach 选项配置父任务,则子任务中的 AttachedToParent 选项不起作用,并且子任务将作为分离的子任务执行。有关详细信息,请参阅附加和分离的子任务
DenyChildAttach 指定任何尝试作为附加的子任务执行(即,使用 AttachedToParent 选项创建)的子任务都无法附加到父任务,会改成作为分离的子任务执行。 有关详细信息,请参阅附加和分离的子任务
HideScheduler 防止环境计划程序被视为已创建任务的当前计划程序。 这意味着像 StartNew 或 ContinueWith 创建任务的执行操作将被视为 Default 当前计划程序。
LongRunning 指定任务将是长时间运行的、粗粒度的操作,涉及比细化的系统更少、更大的组件。 它会向 TaskScheduler 提示,过度订阅可能是合理的。 可以通过过度订阅创建比可用硬件线程数更多的线程。 它还将提示任务计划程序:该任务需要附加线程,以使任务不阻塞本地线程池队列中其他线程或工作项的向前推动。
None 指定应使用默认行为。
PreferFairness 提示 TaskScheduler 以一种尽可能公平的方式安排任务,这意味着较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。
RunContinuationsAsynchronously 强制异步执行添加到当前任务的延续任务。请注意,RunContinuationsAsynchronously 成员在以 .NET Framework 4.6 开头的 TaskCreationOptions 枚举中可用。
static void Main(string[] args)
{
    // 使用构造方法创建任务
    var t1 = new Task(() => TaskMethod("Task 1"));
    var t2 = new Task(() => TaskMethod("Task 2"));

    // 需要手动启动
    t2.Start();
    t1.Start();

    // 使用Task.Run 方法启动任务  不需要手动启动
    Task.Run(() => TaskMethod("Task 3"));

    // 使用 Task.Factory.StartNew方法 启动任务 实际上就是Task.Run
    Task.Factory.StartNew(() => TaskMethod("Task 4"));

    // 在StartNew的基础上 添加 TaskCreationOptions.LongRunning 告诉 Factory该任务需要长时间运行
    // 那么它就会可能会创建一个 非线程池线程来执行任务  
    Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);

    ReadLine();
}

static void TaskMethod(string name)
{
    WriteLine($"任务 {name} 运行,线程 id {CurrentThread.ManagedThreadId}. 是否为线程池线程: {CurrentThread.IsThreadPoolThread}.");
}

运转结果如下图所示。

威尼斯人线上娱乐 7

Async /Await

在.NET Framework 4.0中添加.NET Framework
4.5中新的异步操作库(async/await),该包由四个库组成:Microsoft.Bcl、Microsoft.Bcl.Async和Microsoft.Bcl.Build。

Install-Package Microsoft.Bcl.Async

注:asp.net
框架必须求升级.net framework框架才能采纳 async/await

假定不行新闻是“Message : Could not load file or assembly ‘System.Core,
Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e,
Retargetable=Yes’ or one of its dependencies. The given assembly name or
codebase was invalid. (Exception from HRESULT: 0x80131047)”,

那供给你去微软官网下载.net4.0的KB2468871补丁来安装。

C# 5引入了异步函数(asynchrnous
function)的概念。平日是指用async修饰符注解的,可

包蕴await表达式的艺术或匿名函数;

async关键字创制了1个状态机,类似于yield
return语句;await关键字只好用来有用async修饰符申明的主意。async修饰符只可以用来重回Task/Task<TResult>或void的法门。await只可以用来调用重临Task/Task<TResult>的措施;await会解除线程的不通,完成调用的任务;等待职责完毕后,获取结果,然后实施await关键字背后的代码;编译器会把await的表明式后的代码应用
Task.ContinueWith
包裹了起来,回调时暗中认可使用当前线程的一块儿上下文职务调度器;如若不应用同样的同步上下文,必须调用Task实例的ConfigureAwait(false)方法;

await msg.Content.ReadAsStringAsync().ConfigureAwait(false);

异步方法的宣示语法与别的措施完全一致,只是要蕴含async上下文关键字。async能够出

近来回来类型以前的其他任务。async修饰符在扭转的代码中并未效果,也可省略不写,它显然发布了您的预想,告诉编写翻译器能够积极寻找await表明式,也得以查找应该转换来异步调用和await表明式的块调用。

调用者和异步方法之间是通过重返值来通讯的。异步函数的回来类型只可以为:

Void
、Task、Task<TResult>;Task和Task<TResult>类型都表示一个也许还未到位的操作。
Task<TResult>继承自Task。二者的界别是,Task<TResult>表示一个重回值为T类型的操作,而Task则不供给发出重返值。在某种意义上,你能够认为Task就是Task<void>类型;

之所以将异步方法设计为可以重回void,是为了和事件处理程序包容。

异步方法签名的约束:全部参数都无法利用out或ref修饰符。

威尼斯人线上娱乐 8

 

API 异步方法


怎么找到像 GetStringAsync 那样帮助异步编程的艺术。 .NET Framework 4.5
包罗使用 async 和 await 的广大成员,它们都已“Async”为后缀和 Task 或
Task<TResult> 的归来类型。
例如,System.IO.Stream 类包括的措施
CopyToAsync、ReadAsync、WriteAsync 等情势以及一头方法 CopyTo、Read 和
Write。

4.1 简介

1.3 使用任务履行基本的操作

在本节中,使用职分履行基本的操作,并且得到职责执行到位后的结果值。本节内容相比较简单,在此不做过多介绍。

以身作则代码如下,在主线程中要获取结果值,常用的法子便是造访task.Result品质,假若义务线程还没执行完毕,那么会阻塞主线程,直到线程执行完。假如职责线程执行达成,那么将一贯得到运算的结果值。

Task 3中,使用了task.Status来打字与印刷线程的气象,线程各类景况的实际意思,将在下一节中介绍。

static void Main(string[] args)
{
    // 直接执行方法 作为参照
    TaskMethod("主线程任务");

    // 访问 Result属性 达到运行结果
    Task<int> task = CreateTask("Task 1");
    task.Start();
    int result = task.Result;
    WriteLine($"运算结果: {result}");

    // 使用当前线程,同步执行任务
    task = CreateTask("Task 2");
    task.RunSynchronously();
    result = task.Result;
    WriteLine($"运算结果:{result}");

    // 通过循环等待 获取运行结果
    task = CreateTask("Task 3");
    WriteLine(task.Status);
    task.Start();

    while (!task.IsCompleted)
    {
        WriteLine(task.Status);
        Sleep(TimeSpan.FromSeconds(0.5));
    }

    WriteLine(task.Status);
    result = task.Result;
    WriteLine($"运算结果:{result}");

    Console.ReadLine();
}

static Task<int> CreateTask(string name)
{
    return new Task<int>(() => TaskMethod(name));
}

static int TaskMethod(string name)
{
    WriteLine($"{name} 运行在线程 {CurrentThread.ManagedThreadId}上. 是否为线程池线程 {CurrentThread.IsThreadPoolThread}");

    Sleep(TimeSpan.FromSeconds(2));

    return 42;
}

运维结果如下,可知Task 1
Task 2均是运作在主线程上,并非线程池线程。

威尼斯人线上娱乐 9

线程


异步方法目的在于成为非阻止操作。异步方法中的 await
表明式在伺机的天职正在运营时,不会阻碍当前线程。相反,表明式在一而再时,注册格局的其余部分并将控件再次回到到异步方法的调用方。

async 和 await
关键字不会招致创制别的线程。
因为异步方法不会在其本身线程上运维,因而它不要求八线程。
只有当方法处于活动状态时,该方式将在当下同步上下文中运营并运用线程上的年华。
能够行使 Task.Run 将占据多量 CPU
的干活移到后台线程,可是后台线程不会援救正在守候结果的长河变为可用状态。

对此异步编制程序而言,该基于异步的艺术优于差不离每一种用例中的现有措施。
具体而言,此方法比 BackgroundWorker 更适用于 IO
绑定的操作,因为此代码更简约且无需提防争用标准化。 结合 Task.Run
使用时,异步编制程序比 BackgroundWorker 更适用于 CPU
绑定的操作,因为异步编制程序将运营代码的协调细节与 Task.Run
传输至线程池的工作分别开来。

 

1.4 组合任务

在本节中,体现了任务之中三个强大的效用,那正是整合职责。通过结合职务可很好的叙述任务与任务之间的异步、同步关系,大大降低了编制程序的难度。

重组职分首固然透过task.ContinueWith()task.WhenAny()task.WhenAll()等和task.GetAwaiter().OnCompleted()方法来落到实处。

在使用task.ContinueWith()主意时,要求小心它也可传递一多如牛毛的枚举选项TaskContinuationOptions,该枚举选项和TaskCreationOptions就如,其具体定义如下表所示。

成员名称 说明
AttachedToParent 如果延续为子任务,则指定将延续附加到任务层次结构中的父级。 只有当延续前面的任务也是子任务时,延续才可以是子任务。 默认情况下,子任务(即由外部任务创建的内部任务)将独立于其父任务执行。 可以使用 TaskContinuationOptions.AttachedToParent 选项以便将父任务和子任务同步。请注意,如果使用 DenyChildAttach 选项配置父任务,则子任务中的 AttachedToParent 选项不起作用,并且子任务将作为分离的子任务执行。有关更多信息,请参见Attached and Detached Child Tasks
DenyChildAttach 指定任何使用 TaskCreationOptions.AttachedToParent 选项创建,并尝试作为附加的子任务执行的子任务(即,由此延续创建的任何嵌套内部任务)都无法附加到父任务,会改成作为分离的子任务执行。 有关详细信息,请参阅附加和分离的子任务
ExecuteSynchronously 指定应同步执行延续任务。 指定此选项后,延续任务在导致前面的任务转换为其最终状态的相同线程上运行。如果在创建延续任务时已经完成前面的任务,则延续任务将在创建此延续任务的线程上运行。 如果前面任务的 CancellationTokenSource 已在一个 finally(在 Visual Basic 中为 Finally)块中释放,则使用此选项的延续任务将在该 finally 块中运行。 只应同步执行运行时间非常短的延续任务。由于任务以同步方式执行,因此无需调用诸如 Task.Wait 的方法来确保调用线程等待任务完成。
HideScheduler 指定由延续通过调用方法(如 Task.RunTask.ContinueWith)创建的任务将默认计划程序 (TaskScheduler.Default) 视为当前的计划程序,而不是正在运行该延续的计划程序。
LazyCancellation 在延续取消的情况下,防止延续的完成直到完成先前的任务。
LongRunning 指定延续将是长期运行的、粗粒度的操作。 它会向 TaskScheduler 提示,过度订阅可能是合理的。
None 如果未指定延续选项,应在执行延续任务时使用指定的默认行为。 延续任务在前面的任务完成后以异步方式运行,与前面任务最终的 Task.Status 属性值无关。 如果延续为子任务,则会将其创建为分离的嵌套任务。
NotOnCanceled 指定不应在延续任务前面的任务已取消的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Canceled,则前面的任务会取消。 此选项对多任务延续无效。
NotOnFaulted 指定不应在延续任务前面的任务引发了未处理异常的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Faulted,则前面的任务会引发未处理的异常。 此选项对多任务延续无效。
NotOnRanToCompletion 指定不应在延续任务前面的任务已完成运行的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.RanToCompletion,则前面的任务会运行直至完成。 此选项对多任务延续无效。
OnlyOnCanceled 指定只应在延续前面的任务已取消的情况下安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Canceled,则前面的任务会取消。 此选项对多任务延续无效。
OnlyOnFaulted 指定只有在延续任务前面的任务引发了未处理异常的情况下才应安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.Faulted,则前面的任务会引发未处理的异常。OnlyOnFaulted 选项可保证前面任务中的 Task.Exception 属性不是 null。 你可以使用该属性来捕获异常,并确定导致任务出错的异常。 如果你不访问 Exception 属性,则不会处理异常。 此外,如果尝试访问已取消或出错的任务的 Result 属性,则会引发一个新异常。此选项对多任务延续无效。
OnlyOnRanToCompletion 指定只应在延续任务前面的任务已完成运行的情况下才安排延续任务。 如果前面任务完成的 Task.Status 属性是 TaskStatus.RanToCompletion,则前面的任务会运行直至完成。 此选项对多任务延续无效。
PreferFairness 提示 TaskScheduler 按任务计划的顺序安排任务,因此较早安排的任务将更可能较早运行,而较晚安排运行的任务将更可能较晚运行。
RunContinuationsAsynchronously 指定应异步运行延续任务。 此选项优先于 TaskContinuationOptions.ExecuteSynchronously。

以身作则代码如下所示,使用ContinueWith()OnCompleted()方法结合了任务来运维,搭配分化的TaskCreationOptionsTaskContinuationOptions来落到实处差异的效益。

static void Main(string[] args)
{
    WriteLine($"主线程 线程 Id {CurrentThread.ManagedThreadId}");

    // 创建两个任务
    var firstTask = new Task<int>(() => TaskMethod("Frist Task",3));
    var secondTask = new Task<int>(()=> TaskMethod("Second Task",2));

    // 在默认的情况下 ContiueWith会在前面任务运行后再运行
    firstTask.ContinueWith(t => WriteLine($"第一次运行答案是 {t.Result}. 线程Id {CurrentThread.ManagedThreadId}. 是否为线程池线程: {CurrentThread.IsThreadPoolThread}"));

    // 启动任务
    firstTask.Start();
    secondTask.Start();

    Sleep(TimeSpan.FromSeconds(4));

    // 这里会紧接着 Second Task运行后运行, 但是由于添加了 OnlyOnRanToCompletion 和 ExecuteSynchronously 所以会由运行SecondTask的线程来 运行这个任务
    Task continuation = secondTask.ContinueWith(t => WriteLine($"第二次运行的答案是 {t.Result}. 线程Id {CurrentThread.ManagedThreadId}. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}"),TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously);

    // OnCompleted 是一个事件  当contiuation运行完成后 执行OnCompleted Action事件
    continuation.GetAwaiter().OnCompleted(() => WriteLine($"后继任务完成. 线程Id {CurrentThread.ManagedThreadId}. 是否为线程池线程 {CurrentThread.IsThreadPoolThread}"));

    Sleep(TimeSpan.FromSeconds(2));
    WriteLine();

    firstTask = new Task<int>(() => 
    {
        // 使用了TaskCreationOptions.AttachedToParent 将这个Task和父Task关联, 当这个Task没有结束时  父Task 状态为 WaitingForChildrenToComplete
        var innerTask = Task.Factory.StartNew(() => TaskMethod("Second Task",5), TaskCreationOptions.AttachedToParent);

        innerTask.ContinueWith(t => TaskMethod("Thrid Task", 2), TaskContinuationOptions.AttachedToParent);

        return TaskMethod("First Task",2);
    });

    firstTask.Start();

    // 检查firstTask线程状态  根据上面的分析 首先是  Running -> WatingForChildrenToComplete -> RanToCompletion
    while (! firstTask.IsCompleted)
    {
        WriteLine(firstTask.Status);

        Sleep(TimeSpan.FromSeconds(0.5));
    }

    WriteLine(firstTask.Status);

    Console.ReadLine();
}

static int TaskMethod(string name, int seconds)
{
    WriteLine($"任务 {name} 正在运行,线程池线程 Id {CurrentThread.ManagedThreadId},是否为线程池线程: {CurrentThread.IsThreadPoolThread}");

    Sleep(TimeSpan.FromSeconds(seconds));

    return 42 * seconds;
}

运维结果如下图所示,与预期结果一律。在那之中使用了task.Status来打字与印刷任务运转的动静,对于task.Status的景色具体意思如下表所示。

成员名称 说明
Canceled 该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 对取消进行了确认,此时该标记处于已发送信号状态;或者在该任务开始执行之前,已向该任务的 CancellationToken 发出了信号。 有关详细信息,请参阅任务取消
Created 该任务已初始化,但尚未被计划。
Faulted 由于未处理异常的原因而完成的任务。
RanToCompletion 已成功完成执行的任务。
Running 该任务正在运行,但尚未完成。
WaitingForActivation 该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划。
WaitingForChildrenToComplete 该任务已完成执行,正在隐式等待附加的子任务完成。
WaitingToRun 该任务已被计划执行,但尚未开始执行。

威尼斯人线上娱乐 10

异步和等候


借使由此 async 修饰符内定某种情势为异步方法,则能够启用以下多少个效益。

  • 标志的异步方法能够动用 await 来钦赐悬挂点。await
    运算符公告编写翻译器异步方法唯有直到等待的异步进度完结才能接二连三通过该点。
    同时,控件重回至异步方法的调用方。 await
    表明式中异步方法的挂起不能使该措施退出,并且 finally 块不会运作。
  • 标记的异步方法自身能够由此调用它的主意等待

异步方法一般包含 await 运算符的1个或八个匹配项,但贫乏 await
表明式不会招致编写翻译器错误。 借使异步方法未使用 await
运算符标记悬挂点,则该格局将作为共同方法执行,不管异步修饰符如何。
编写翻译器将为此类措施发表一个警戒。

Async 、async、Await 和 await 都以上下文关键字。
有关越来越多音讯和示范,请参见以下核心:

  • async
  • await

线程池也正是线程和用户之间的3个抽象层,向程序员隐藏了运用线程的细节,使得程序员专心处理程序逻辑,而不是各个线程难点。

1.5 将APM方式转换为天职

在前方的章节中,介绍了依据IAsyncResult接口完毕了BeginXXXX/EndXXXX艺术的就叫APM方式。APM方式尤其古老,那么怎么着将它转换为TAP形式呢?对于常见的二种APM格局异步职责,我们一般采用选拔Task.Factory.FromAsync()措施来兑现将APM模式转换为TAP模式

演示代码如下所示,相比较简单不作过多介绍。

static void Main(string[] args)
{
    int threadId;
    AsynchronousTask d = Test;
    IncompatibleAsychronousTask e = Test;

    // 使用 Task.Factory.FromAsync方法 转换为Task
    WriteLine("Option 1");
    Task<string> task = Task<string>.Factory.FromAsync(d.BeginInvoke("异步任务线程", CallBack, "委托异步调用"), d.EndInvoke);

    task.ContinueWith(t => WriteLine($"回调函数执行完毕,现在运行续接函数!结果:{t.Result}"));

    while (!task.IsCompleted)
    {
        WriteLine(task.Status);
        Sleep(TimeSpan.FromSeconds(0.5));
    }
    WriteLine(task.Status);
    Sleep(TimeSpan.FromSeconds(1));

    WriteLine("----------------------------------------------");
    WriteLine();

    // 使用 Task.Factory.FromAsync重载方法 转换为Task
    WriteLine("Option 2");

    task = Task<string>.Factory.FromAsync(d.BeginInvoke,d.EndInvoke,"异步任务线程","委托异步调用");

    task.ContinueWith(t => WriteLine($"任务完成,现在运行续接函数!结果:{t.Result}"));

    while (!task.IsCompleted)
    {
        WriteLine(task.Status);
        Sleep(TimeSpan.FromSeconds(0.5));
    }
    WriteLine(task.Status);
    Sleep(TimeSpan.FromSeconds(1));

    WriteLine("----------------------------------------------");
    WriteLine();

    // 同样可以使用 FromAsync方法 将 BeginInvoke 转换为 IAsyncResult 最后转换为 Task
    WriteLine("Option 3");

    IAsyncResult ar = e.BeginInvoke(out threadId, CallBack, "委托异步调用");
    task = Task<string>.Factory.FromAsync(ar, _ => e.EndInvoke(out threadId, ar));

    task.ContinueWith(t => WriteLine($"任务完成,现在运行续接函数!结果:{t.Result},线程Id {threadId}"));

    while (!task.IsCompleted)
    {
        WriteLine(task.Status);
        Sleep(TimeSpan.FromSeconds(0.5));
    }
    WriteLine(task.Status);

    ReadLine();
}

delegate string AsynchronousTask(string threadName);
delegate string IncompatibleAsychronousTask(out int threadId);

static void CallBack(IAsyncResult ar)
{
    WriteLine("开始运行回调函数...");
    WriteLine($"传递给回调函数的状态{ar.AsyncState}");
    WriteLine($"是否为线程池线程:{CurrentThread.IsThreadPoolThread}");
    WriteLine($"线程池工作线程Id:{CurrentThread.ManagedThreadId}");
}

static string Test(string threadName)
{
    WriteLine("开始运行...");
    WriteLine($"是否为线程池线程:{CurrentThread.IsThreadPoolThread}");
    Sleep(TimeSpan.FromSeconds(2));

    CurrentThread.Name = threadName;
    return $"线程名:{CurrentThread.Name}";
}

static string Test(out int threadId)
{
    WriteLine("开始运行...");
    WriteLine($"是否为线程池线程:{CurrentThread.IsThreadPoolThread}");
    Sleep(TimeSpan.FromSeconds(2));

    threadId = CurrentThread.ManagedThreadId;
    return $"线程池线程工作Id是:{threadId}";
}

运作结果如下图所示。

威尼斯人线上娱乐 11

回去类型和参数


.NET Framework 异步编制程序中异步方法一般再次来到 Task 或 Task<TResult>。
在异步方法中,await 运算符应用于通过调用另一个异步方法再次回到的职责。

一经措施包罗 Return (Visual Basic) 或钦命项目 TResult 的操作数的 return
(C#) 语句,则将 Task<TResult> 钦定为回到类型。

假若艺术不含任何 return 语句或含有不回去操作数的 return 语句,则将 Task
用作重临类型。

上面包车型的士演示演示如何表明并调用可重返 Task<TResult> 或 Task 的法门。

// Signature specifies Task<;TResult>

async Task<;int> TaskOfTResult_MethodAsync()

{

    int hours;

    // . . .

    // Return statement specifies an integer result.

    return hours;

}

 

// Calls to TaskOfTResult_MethodAsync

Task<;int> returnedTaskTResult = TaskOfTResult_MethodAsync();

int intResult = await returnedTaskTResult;

// or, in a single statement

int intResult = await TaskOfTResult_MethodAsync();

// Signature specifies Task

async Task Task_MethodAsync()

{

    // . . .

    // The method has no return statement.  

}

 

// Calls to Task_MethodAsync

Task returnedTask = Task_MethodAsync();

await returnedTask;

// or, in a single statement

await Task_MethodAsync();

各个再次来到的职务表示正在展开的劳作。
职务可包裹有关异步进程意况的消息,要是未成功,则最终会卷入来自进程的末梢结出或进程引发的要命。

异步方法还足以是 Sub 方法 (Visual Basic) 或具有 void 重临类型 (C#)。
该重返类型主要用来定义须求 void 重回类型的事件处理程序。
异步事件处理程序常常作为异步程序的初阶点。

不可能等待为 Sub 程序或持有 void
重返类型的异步方法,并且无效的回到方法的调用方非常的小概捕获该措施引发的别的至极。

异步方法不能够表明 Visual Basic 中的 ByRef 参数或 C# 中的 ref 或 out
参数,但此办法能够调用具有此类参数的法门。

至于越多音讯和示范,请参见异步重临类型(C# 和 Visual Basic)。
有关怎么着在异步方法中捕捉分外的更多新闻,请参见 try-catch(C# 参考)或
Try…Catch…Finally 语句 (Visual Basic)。

Windows 运维时编制程序中的异步 API 具有下列重返类型之一,它就像于任务:

  • IAsyncOperation,它对应于 Task<TResult>
  • IAsyncAction,它对应于 Task
  • IAsyncActionWithProgress
  • IAsyncOperationWithProgress

然而使用线程池也很复杂。有三个难题存在:

1.6 将EAP方式转换为任务

在上几章中有关联,通过BackgroundWorker类经过事件的主意贯彻的异步,大家叫它EAP形式。那么哪些将EAP格局转换为天职吗?很简单,大家只须求通过TaskCompletionSource类,即可将EAP格局转换为天职。

演示代码如下所示。

static void Main(string[] args)
{
    var tcs = new TaskCompletionSource<int>();

    var worker = new BackgroundWorker();
    worker.DoWork += (sender, eventArgs) =>
    {
        eventArgs.Result = TaskMethod("后台工作", 5);
    };

    // 通过此方法 将EAP模式转换为 任务
    worker.RunWorkerCompleted += (sender, eventArgs) =>
    {
        if (eventArgs.Error != null)
        {
            tcs.SetException(eventArgs.Error);
        }
        else if (eventArgs.Cancelled)
        {
            tcs.SetCanceled();
        }
        else
        {
            tcs.SetResult((int)eventArgs.Result);
        }
    };

    worker.RunWorkerAsync();

    // 调用结果
    int result = tcs.Task.Result;

    WriteLine($"结果是:{result}");

    ReadLine();
}

static int TaskMethod(string name, int seconds)
{
    WriteLine($"任务{name}运行在线程{CurrentThread.ManagedThreadId}上. 是否为线程池线程{CurrentThread.IsThreadPoolThread}");

    Sleep(TimeSpan.FromSeconds(seconds));

    return 42 * seconds;
}

运作结果如下图所示。

威尼斯人线上娱乐 12

参考资料


  • Microsoft Developer Network 基于任务的异步格局(TAP)
  • 行使 Async 和 Await
    的异步编制程序
  • 异步程序中的控制流

 

下载 Demo

下载 Demo TPL 与 AMP 和 EAP 结合

①获取线程池中的工作线程的结果相比难

1.7 实现裁撤选项

在TAP形式中,完毕废除选项和前边的异步情势一样,都以使用CancellationToken来兑现,不过分裂的是Task构造函数允许传入3个CancellationToken,从而在任务实际运营从前裁撤它。

示范代码如下所示。

static void Main(string[] args)
{
    var cts = new CancellationTokenSource();
    // new Task时  可以传入一个 CancellationToken对象  可以在线程创建时  变取消任务
    var longTask = new Task<int>(() => TaskMethod("Task 1", 10, cts.Token), cts.Token);
    WriteLine(longTask.Status);
    cts.Cancel();
    WriteLine(longTask.Status);
    WriteLine("第一个任务在运行前被取消.");

    // 同样的 可以通过CancellationToken对象 取消正在运行的任务
    cts = new CancellationTokenSource();
    longTask = new Task<int>(() => TaskMethod("Task 2", 10, cts.Token), cts.Token);
    longTask.Start();

    for (int i = 0; i < 5; i++)
    {
        Sleep(TimeSpan.FromSeconds(0.5));
        WriteLine(longTask.Status);
    }
    cts.Cancel();
    for (int i = 0; i < 5; i++)
    {
        Sleep(TimeSpan.FromSeconds(0.5));
        WriteLine(longTask.Status);
    }

    WriteLine($"这个任务已完成,结果为{longTask.Result}");

    ReadLine();
}

static int TaskMethod(string name, int seconds, CancellationToken token)
{
    WriteLine($"任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}");

    for (int i = 0; i < seconds; i++)
    {
        Sleep(TimeSpan.FromSeconds(1));
        if (token.IsCancellationRequested)
        {
            return -1;
        }
    }

    return 42 * seconds;
}

运维结果如下图所示,那里需求专注的是,借使是在义务履行在此以前撤除了职务,那么它的末梢状态是Canceled。假使是在实施进度中收回职分,那么它的情状是RanCompletion

威尼斯人线上娱乐 13

②达成线程池中劳作线程执行的时序难点

1.8 处理职务中的卓殊

在职务中,处理非凡和别的异步格局处理极度类似,假使能在所发出尤其的线程中处理,那么毫无在其它市方处理。不过对于部分不足预期的丰裕,那么能够通过两种方法来拍卖。

能够由此走访task.Result属性来处理十分,因为访问那些天性的Get方式会使当前线程等待直到该职责实现,并将丰富传播给当下线程,那样就足以因此try catch语句块来捕获极度。其它利用task.GetAwaiter().GetResult()方法和第使用task.Result类似,同样能够捕获至极。假如是要捕获多少个职责中的非常错误,那么能够通过ContinueWith()方法来处理。

切切实实哪些完毕,演示代码如下所示。

static void Main(string[] args)
{
    Task<int> task;
    // 在主线程中调用 task.Result task中的异常信息会直接抛出到 主线程中
    try
    {
        task = Task.Run(() => TaskMethod("Task 1", 2));
        int result = task.Result;
        WriteLine($"结果为: {result}");
    }
    catch (Exception ex)
    {
        WriteLine($"异常被捕捉:{ex.Message}");
    }
    WriteLine("------------------------------------------------");
    WriteLine();

    // 同上 只是访问Result的方式不同
    try
    {
        task = Task.Run(() => TaskMethod("Task 2", 2));
        int result = task.GetAwaiter().GetResult();
        WriteLine($"结果为:{result}");
    }
    catch (Exception ex)
    {
        WriteLine($"异常被捕捉: {ex.Message}");
    }
    WriteLine("----------------------------------------------");
    WriteLine();

    var t1 = new Task<int>(() => TaskMethod("Task 3", 3));
    var t2 = new Task<int>(() => TaskMethod("Task 4", 4));

    var complexTask = Task.WhenAll(t1, t2);
    // 通过ContinueWith TaskContinuationOptions.OnlyOnFaulted的方式 如果task出现异常 那么才会执行该方法
    var exceptionHandler = complexTask.ContinueWith(t => {
        WriteLine($"异常被捕捉:{t.Exception.Message}");
        foreach (var ex in t.Exception.InnerExceptions)
        {
            WriteLine($"-------------------------- {ex.Message}");
        }
    },TaskContinuationOptions.OnlyOnFaulted);

    t1.Start();
    t2.Start();

    ReadLine();
}

static int TaskMethod(string name, int seconds)
{
    WriteLine($"任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}");

    Sleep(TimeSpan.FromSeconds(seconds));
    // 人为抛出一个异常
    throw new Exception("Boom!");
    return 42 * seconds;
}

运作结果如下所示,需求注意的是,要是在ContinueWith()主意中抓获多少个任务发生的不行,那么它的可怜类型是AggregateException,具体的卓殊新闻包涵在InnerExceptions其间,要小心和InnerException区分。

威尼斯人线上娱乐 14

综上,我们在第2章中提过的异步编制程序模型和基于事件的异步编制程序模型,这几个格局使得获取结果尤其不难,传播也更自在,然则在进行几个异步操作结合的时候,还须求编写制定大批量的代码。对于第①个难题.NET 4.0提议了一个新的关于异步操作的API。叫做职分并行库(Task Parallel Library
简称 TPL)。

1.9 互动运转任务

本节中首要介绍了两个艺术的施用,3个是伺机组中全体职责都实施实现的Task.WhenAll()办法,另贰个是假如组中一个主意执行完成都执行的Task.WhenAny()方法。

具体运用,如下演示代码所示。

static void Main(string[] args)
{
    // 第一种方式 通过Task.WhenAll 等待所有任务运行完成
    var firstTask = new Task<int>(() => TaskMethod("First Task", 3));
    var secondTask = new Task<int>(() => TaskMethod("Second Task", 2));

    // 当firstTask 和 secondTask 运行完成后 才执行 whenAllTask的ContinueWith
    var whenAllTask = Task.WhenAll(firstTask, secondTask);
    whenAllTask.ContinueWith(t => WriteLine($"第一个任务答案为{t.Result[0]},第二个任务答案为{t.Result[1]}"), TaskContinuationOptions.OnlyOnRanToCompletion);

    firstTask.Start();
    secondTask.Start();

    Sleep(TimeSpan.FromSeconds(4));

    // 使用WhenAny方法  只要列表中有一个任务完成 那么该方法就会取出那个完成的任务
    var tasks = new List<Task<int>>();
    for (int i = 0; i < 4; i++)
    {
        int counter = 1;
        var task = new Task<int>(() => TaskMethod($"Task {counter}",counter));
        tasks.Add(task);
        task.Start();
    }

    while (tasks.Count > 0)
    {
        var completedTask = Task.WhenAny(tasks).Result;
        tasks.Remove(completedTask);
        WriteLine($"一个任务已经完成,结果为 {completedTask.Result}");
    }

    ReadLine();
}

static int TaskMethod(string name, int seconds)
{
    WriteLine($"任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}");

    Sleep(TimeSpan.FromSeconds(seconds));
    return 42 * seconds;
}

运作结果如下图所示。

威尼斯人线上娱乐 15

 

1.10 使用TaskScheduler配置任务执行

Task中,负责职分调度是TaskScheduler目的,FCL提供了七个派生自TaskScheduler的类型:线程池任务调度器(Thread
Pool Task Scheduler)
同步上下文职分调度器(Synchronization
Scheduler)
。暗中同意情况下具有应用程序都使用线程池职分调度器,可是在UI组件中,不使用线程池中的线程,防止跨线程更新UI,供给选用同步上下文义务调度器。能够经过执行TaskSchedulerFromCurrentSynchronizationContext()静态方法来收获对一起上下文职分调度器的引用。

以身作则程序如下所示,为了延时一并上下文任务调度器,大家这次利用WPF来成立项目。

MainWindow.xaml 代码如下所示。

<Window x:Class="Recipe9.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Recipe9"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <TextBlock Name="ContentTextBlock" HorizontalAlignment="Left" Margin="44,134,0,0" VerticalAlignment="Top" Width="425" Height="40"/>
        <Button Content="Sync" HorizontalAlignment="Left" Margin="45,190,0,0" VerticalAlignment="Top" Width="75" Click="ButtonSync_Click"/>
        <Button Content="Async" HorizontalAlignment="Left" Margin="165,190,0,0" VerticalAlignment="Top" Width="75" Click="ButtonAsync_Click"/>
        <Button Content="Async OK" HorizontalAlignment="Left" Margin="285,190,0,0" VerticalAlignment="Top" Width="75" Click="ButtonAsyncOK_Click"/>
    </Grid>
</Window>

MainWindow.xaml.cs 代码如下所示。

/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    // 同步执行 计算密集任务 导致UI线程阻塞
    private void ButtonSync_Click(object sender, RoutedEventArgs e)
    {
        ContentTextBlock.Text = string.Empty;

        try
        {
            string result = TaskMethod().Result;
            ContentTextBlock.Text = result;
        }
        catch (Exception ex)
        {
            ContentTextBlock.Text = ex.InnerException.Message;
        }
    }

    // 异步的方式来执行 计算密集任务 UI线程不会阻塞 但是 不能跨线程更新UI 所以会有异常
    private void ButtonAsync_Click(object sender, RoutedEventArgs e)
    {
        ContentTextBlock.Text = string.Empty;
        Mouse.OverrideCursor = Cursors.Wait;

        Task<string> task = TaskMethod();
        task.ContinueWith(t => {
            ContentTextBlock.Text = t.Exception.InnerException.Message;
            Mouse.OverrideCursor = null;
        }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
    }

    // 通过 异步 和 FromCurrentSynchronizationContext方法 创建了线程同步的上下文  没有跨线程更新UI 
    private void ButtonAsyncOK_Click(object sender, RoutedEventArgs e)
    {
        ContentTextBlock.Text = string.Empty;
        Mouse.OverrideCursor = Cursors.Wait;
        Task<string> task = TaskMethod(TaskScheduler.FromCurrentSynchronizationContext());

        task.ContinueWith(t => Mouse.OverrideCursor = null,
            CancellationToken.None,
            TaskContinuationOptions.None,
            TaskScheduler.FromCurrentSynchronizationContext());
    }

    Task<string> TaskMethod()
    {
        return TaskMethod(TaskScheduler.Default);
    }

    Task<string> TaskMethod(TaskScheduler scheduler)
    {
        Task delay = Task.Delay(TimeSpan.FromSeconds(5));

        return delay.ContinueWith(t =>
        {
            string str = $"任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}";

            Console.WriteLine(str);

            ContentTextBlock.Text = str;
            return str;
        }, scheduler);
    }
}

运作结果如下所示,从左至右依次单击按钮,前八个按钮将会吸引那多少个。
威尼斯人线上娱乐 16

现实信息如下所示。

威尼斯人线上娱乐 17

TPL能够看成线程池之上的又二个抽象层,其对程序员隐藏了与线程池交互的平底代码,并提供了更有益的细粒度的API。

参照书籍

正文主要参照了以下几本书,在此对这一个笔者表示诚挚的多谢,感激您们为.Net的发扬所做的孝敬!

  1. 《CLR via C#》
  2. 《C# in Depth Third Edition》
  3. 《Essential C# 6.0》
  4. 《Multithreading with C# Cookbook Second Edition》
  5. 《C#多线程编制程序实战》

源码下载点击链接
以身作则源码下载

TPL的着力概念是任务。贰个职责代表了2个异步操作,该操作能够接纳多样办法运转,能够接纳或不利用独立线程运维。

作者水平有限,借使不当欢迎各位批评指正!

理所当然想趁待业时期的时光读完《Multithreading with C# 库克book Second
艾德ition》那本书,并且享受做的连锁笔记;可是出于作者近期职业规划和人体原因,或许近来都未曾时间来更新这些体系,没办法实现几天一更。请大家多多包蕴!可是小编一定会将那个类别全体立异完结的!谢谢大家的支撑!

一个职责能够有三种艺术和此外职分组合起来。例如,可以同时实施八个职责,等待全数职责完成,然后运维贰个任务对前边全部的任务结果举办一些盘算。TPL与前边的方式相比,当中1个要害优势是其颇具用于组合任务的便民的API。

处理任务中的至极结果也有二种方法。二个义务能够由八种任务组成,这个职分也得以有各自的子职务,所以有一个AggregateException的概念。那种十分能够捕获底层职务之中的享有尤其,并允许单独处理这一个越发。

C#5.0中得以应用await和async关键词以平滑的,舒服的主意开始展览操作职务。

 

4.2 创立职分

创立职责有二种格局:

1.一贯开立任务实例,通超过实际例方法Start方法来运行职务

2.应用静态方法Task.Run和Task.Factory.StartNew来成立职责,两者都不须求展现的调用start方法运维义务,不一致在于前者是继承者的一种飞速情势,后者能够利用附加的选项。

例:
1     class Program
2     {
3         static void Main(string[] args)
4         {
5             //第一种直接创建任务实例,需要用start方法来启动任务
6             var t1 = new Task(() => TaskMethod("Task 1"));
7             var t2 = new Task(() => TaskMethod("Task 2"));
8             t2.Start();
9             t1.Start();
10           //第二种通过Task.Factory.StartNew来创建任务
11           //这里Run方法只是Task.Factory.StartNew的一个快捷方式,Task.Factory.StartNew可以添加附加选项
12           Task.Run(() => TaskMethod("Task 3"));
13           Task.Factory.StartNew(() => TaskMethod("Task 4"));
14           //我们标记了该任务是长时间任务,结果该任务没有使用线程池,而是在单独的线程中运行
15           Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);
16           Thread.Sleep(TimeSpan.FromSeconds(1));
17         }
18 
19         static void TaskMethod(string name)
20         {
21             Console.WriteLine(
22                                 "Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
23                                  name,
24                                 Thread.CurrentThread.ManagedThreadId, 
25                                 Thread.CurrentThread.IsThreadPoolThread);
26         }
27   }

威尼斯人线上娱乐 18

※由于没有对职分的时序做处理,所以屡屡推行每贰次都也许不一致。

※Task5选拔的是独自线程的艺术来运营,然则依照运营该任务的日前的职分调度程序(task scheduler),运转格局也许会差异。

 

4.3利用职务履行基本的操作

主要介绍怎么着从职分中获得结果。

1     class Program
2     {
3         static void Main(string[] args)
4         {
5              //启动主线程
6              TaskMethod("Main Thread Task");
7              //创建一个任务Task1,进行线程的同步
8              Task<int> task = CreateTask("Task 1");
9              task.Start();
10             //阻塞主线程,直到线程执行完成
11             int result = task.Result;
12             Console.WriteLine("Result is: {0}", result);
13 
14             //创建Taks2,使用RunSynchronously()方法进行同步
15             task = CreateTask("Task 2");
16             task.RunSynchronously();
17             result = task.Result;
18             Console.WriteLine("Result is: {0}", result);
19 
20             //创建Task3,此时不进行主线程的阻塞
21             task = CreateTask("Task 3");
22             Console.WriteLine(task.Status);
23             task.Start();
24 
25             //循环打印task的状态,直到任务完成
26             while (!task.IsCompleted)
27             {
28                 Console.WriteLine(task.Status);
29                 Thread.Sleep(TimeSpan.FromSeconds(0.5));
30             } 
31             
32             Console.WriteLine(task.Status);
33             result = task.Result;
34             Console.WriteLine("Result is: {0}", result);
35         }
36 
37         //创建一个新任务
38         static Task<int> CreateTask(string name)
39         {
40             return new Task<int>(() => TaskMethod(name));
41         }
42 
43         //任务需要处理的方法
44         static int TaskMethod(string name)
45         {
46             Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
47             name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
48             Thread.Sleep(TimeSpan.FromSeconds(2));
49             return 42;
50         }
51    }

履行结果:

威尼斯人线上娱乐 19

 

4.4 组合职分

此间小编会学习到什么将职分进行理并了结合,以及父子任务之间的履行。废话不说,有码

实例1:

1     class Program
2     {
3         static void Main(string[] args)
4         {
5             //打印主线程
6             TaskMethod("Main Task", 1);
7             //创建两个任务
8             var firstTask = new Task<int>(() => TaskMethod("First Task", 3));
9             var secondTask = new Task<int>(() => TaskMethod("Second Task", 2));
10 
11             //设置firstTask的后续操作
12             firstTask.ContinueWith(
13                 t => Console.WriteLine("The first answer is {0}. Thread id {1}, is thread pool thread: {2}",
14                     t.Result, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread),
15                 TaskContinuationOptions.OnlyOnRanToCompletion);
16 
17              //启动两个任务
18             firstTask.Start();
19             secondTask.Start();
20             //延时4秒,足够两个任务完成的时间※↓这句是关键
21             Thread.Sleep(TimeSpan.FromSeconds(4));
22 
23             //为secondTask设置一个后续操作,TaskContinuationOptions.ExecuteSynchronously尝试同步方式执行后续操作
24             Task continuation = secondTask.ContinueWith(
25                 t => Console.WriteLine("The second answer is {0}. Thread id {1}, is thread pool thread: {2}",
26                     t.Result, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread),
27                 TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously);
28 
29             //为之前的后续操作也定义一个后续操作,这里使用了C#5.0的方法GetAwaiter().OnCompleted()
30             continuation.GetAwaiter().OnCompleted(
31                 () => Console.WriteLine("Continuation Task Completed! Thread id {0}, is thread pool thread: {1}",
32                     Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
33 
34             Thread.Sleep(TimeSpan.FromSeconds(2));
35             Console.WriteLine();
36 
37             Thread.Sleep(TimeSpan.FromSeconds(10));
38         }
39 
40         static int TaskMethod(string name, int seconds)
41         {
42             Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
43                 name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
44             Thread.Sleep(TimeSpan.FromSeconds(seconds));
45             return 42 * seconds;
46         }
47  }

威尼斯人线上娱乐 20

此间大家看出secondTask的存在延续操作没有应用到线程池,为何吗?

表达:由地方的代码我们看来,使用了TaskContinuationOptions.ExecuteSynchronously尝试同步格局履行后续操作,假如三番五次操作时间非常的短暂,使用方面包车型大巴章程要命有成效的,因为放置在主线程进行运作要比放置在线程池中运作要快,那干什么会产出那样的景观呢,正是上面标记的延时期码的功德,那段延时期码使得SecondTask后续操作正好获得了前边职分履行的结果。现在自家把  Thread.Sleep(TimeSpan.FromSeconds(4));注释掉再试一下,结果如下:

威尼斯人线上娱乐 21

感到就像茶楼打饭,五个人用餐,A帮B打饭。

先是种是:A打完饭后,发现B刚来,就直接把饭给了B,然后B直接吃了

第1种是:A打饭的时候,B正好也来了,于是多个人一起站队,A打完饭后再把饭给了B

 

例2:演示了刹那间父子职分之间的关系。

1 class Program
2     {
3         static void Main(string[] args)
4         {
5              //创建一个父任务
6              var firstTask = new Task<int>(() =>
7             {
8                 //创建一个子任务,使用TaskCreationOptions.AttachedToParent来标识
9                 var innerTask = Task.Factory.StartNew(
10                                         () => TaskMethod("Second Task", 5), 
11                                         TaskCreationOptions.AttachedToParent);
12               //创建一个子任务的后续操作,该后续操作也会影响父任务
13                innerTask.ContinueWith(
14                                         t => TaskMethod("Third Task", 2), 
15                                         TaskContinuationOptions.AttachedToParent);
16                 return TaskMethod("First Task", 2);
17             });
18 
19             //启动任务
20             firstTask.Start();
21 
22             //循环打印任务的状态
23             while (!firstTask.IsCompleted)
24             {
25                 Console.WriteLine(firstTask.Status);
26                 Thread.Sleep(TimeSpan.FromSeconds(0.5));
27             }
28             Console.WriteLine(firstTask.Status);
29 
30             Thread.Sleep(TimeSpan.FromSeconds(10));
31         }
32 
33         static int TaskMethod(string name, int seconds)
34         {
35             Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
36                 name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
37             Thread.Sleep(TimeSpan.FromSeconds(seconds));
38             return 42 * seconds;
39         }

威尼斯人线上娱乐 22

下面结果呈现,父任务必须等待全体的子职分成功才能不辱任务,不过看不出来他们是3头依然异步执行的。因为从First Task和Sencod
Task它们中间的周转时序上也看不出来他们是阿爸执行完了再举办的子任务,所以自身认为把父职责的时间调长一点,那回自家让父义务履行10s

修改:

   return TaskMethod(“First Task”, 2);  →   return TaskMethod(“First Task”,
10);

结果如下

威尼斯人线上娱乐 23

那回显得的都以firstTask的Running状态,所以理应能自然父子之间暗许情形下也是异步执行的。因为父任务必要求等子任务全甘休才能形成。

 

 


相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图