应用任务并行库,当中的十六线程_职分并行库

目录

复习:

 IO操作的MDA(Direct memory access)情势:直接待上访谈内部存款和储蓄器,是一种不通过CPU而平昔举行内部存款和储蓄器数据存款和储蓄的数据沟通形式,大约能够不损耗CPU的能源;
 CLEvoque所提供的异步编制程序模型就是充足利用硬件的DMA效率来释放CPU的下压力;使用线程池举办管理,异步将专业移交给线程池中的某个职业线程来产生,直到异步达成,异步才会由此回调的主意公告线程池,让CLEnclave响应异步完结;

正文内容

  • 概述
  • 编辑异步方法
  • 异步程序中的调节流
  • 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


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

异步编制程序形式------行使委托和线程池完成的方式

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

EAP 基于事件的异步编制程序形式,Event-based Asynchronous Pattern  C#2.0

TAP 基于职责的异步编制程序形式,Task-based Asynchronous Pattern    C#4.0

Asyncawait简化异步编制程序;职责并行库,Task Parallel Library     C#5

下载 Demo TPL 与 APM 和 EAP 结合(APM 和 EAP 那五个职业异步格局已经不能够适应多核时期,但从前用那三种艺术写的代码如何是好?——把它们退换一下,跟 TPL 结合)

本连串首页链接:[C#四线程编程类别(一)- 简单介绍 ]

2.EAP(基于事件的异步编制程序情势):那一个大家在.net中应用到了BackgroudWorker组件,使用方法是透过事件绑定管理的主意。

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委托仅能够调用切合一定情势的方法(独有一个参数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线程不封堵并终止响应客户输入。不过,异步操作实现时,由于是用三个线程池线程完结的,而线程池线程不可能更新UI控件。为化解那一个难题,FCL定义叁个System.Threading.SynchronizationContext(线程同步上下文)的基类,其派生对象承担将三个应用程序模型连接到它的线程管理模型。

GUI线程皆有贰个和它事关的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.第55中学,任何具有 async 关键字的方法都被视作是一种异步方法,並且 C# 会施行供给的转移,以通过 TAP 来异步实现该方法。 异步方法应返回System.Threading.Tasks.Task 或 System.Threading.Tasks.Task<TResult> 对象。
  • 手动生成 TAP 方法。也能够手动完毕 TAP 格局,以更好地调控完毕。编写翻译器信任从 System.Threading.Tasks 命名空间公开的共用外围应用和 System.Runtime.CompilerServices 命名空间中扶持的花色。 如要本身达成 TAP,你供给成立八个TaskCompletionSource<TResult> 对象、实行异步操作,并在操作完毕时,调用 SetResult、SetException、SetCanceled 方法,或调用那个方法之一的Try版本。 手动完毕 TAP 方法时,需在所代表的异步操作实现时成功生成的职务。 比方:
  • 混合方法。您大概发现手动达成 TAP 情势、但将完毕大旨逻辑委托给编译器的这种艺术很有用。 举个例子,当您想要验证编写翻译器生成的异步方法之外的实参时,恐怕要求使用这种混合方法,以便至极能够转义到该格局的直接调用方并不是通过 System.Threading.Tasks.Task 对象被公开:

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


3.TPL(基于职责的异步编制程序情势):这么些就能够用到职分并行库。

EAP

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

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

   FCL中独有十六个类别达成了EAP方式,常常有二个XXXAsync方法和二个应和的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:向那几个事件登记的主意应该包蕴计算范围的代码。这么些事件由多少个线程池线程调用RunWorkerAsync(四个重载方法,带参的措施是向DoWork登记的措施的DoWorkEventArgs参数对象的Argument属性传值,只好在登记的艺术中(如e.Argument)获取,Result属性必得设置成计算范围的操作希望回到的值)时引发;

ProgressChanged:向那么些事件登记的主意应该富含使用进程消息来更新UI的代码。那几个事件三翻五次在GUI线程上引发。DoWork登记的措施必得定时调用BackgroundWorker的ReportProgress方法来诱惑ProgressChanged事件;

RunWorkerCompleted:向那个事件登记的办法应该蕴含使用总括范围操作的结果对UI举行创新的代码。这些事件三番四遍在GUI线程上吸引。Result获取表示异步操作的结果;

公家性质:CancellationPending(标记是或不是已呼吁撤废后台操作)、IsBusy(标志是不是正在运作异步操作)、WorkReportsProgress(获取/设置是或不是报告进程更新)、WorkerSupportsCancellation(获取/设置是不是补助异步撤销)

国有艺术:CancelAsync(哀告撤除挂起的后台操作)、ReportProgress、RunWorkerAsync

异常

非常不会抛出。在XXXCompleted事件管理方法中,必需询问AsyncCompletedEventArgs的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("rnLength of the downloaded string: {0}.rn", 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 . . . . . . .rn";

}

施行结果:

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 检索结果。

上面总括让三个示范成为异步方法的特点:

  • 措施具名包涵三个 async 修饰符。
  • 依照约定,异步方法的名称以“Async”后缀结尾。
  • 重返类型为下列项目之一:
    • 比如你的不二秘籍有 TResult 类型的归来语句,则为 Task<TResult>。
    • 若果您的办法未有重回语句,则为 Task。
    • 如若您编写的是异步事件管理程序,则为 Void(Visual Basic 中为 Sub)。
  • 方法日常富含起码二个 await 表明式,该表明式标志二个点,在该点上,直到等待的异步操作达成章程技术三翻五次。同一时候,将艺术挂起,何况控件重返到方法的调用方。

在异步方法中,可应用提供的要紧字和系列来提示需求完结的操作,且编译器会成功其余操作,在那之中囊括不断追踪控件以挂起方法重返等待点时发出的动静。 一些好端端流程(举个例子,循环和非常管理)在价值观异步代码中拍卖起来可能特别不方便。 在异步方法中,成分的编排频率与一起建设方案一样且此主题材料获得解决。

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

当协会一个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事件注册回调函数。每当四个Task被垃圾回收时,假如存在二个从未有过在乎到的要命,CL奥迪Q5的实现器线程会引发这一个事件。

可在事变回调函数中调用UnobservedTaskExceptionEventArgs对象的SetObserved() 方法来提出已经管理好了那么些,进而阻碍CL逍客终止线程。然则并不引入这么做,宁愿终止进度也绝不带着已经损坏的情形继续运营。

异步程序中的调节流


异步编程中最需弄清的是调整流是何许从章程移动到方法。

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.rn"  

               "           Calling AccessTheWebAsync.rn";

 

           int contentLength = await AccessTheWebAsync();

 

           resultsTextBox.Text  =

               String.Format("rn6:   Length of the downloaded string: {0}.rn", contentLength);

       }

 

       async Task<int> AccessTheWebAsync()

       {

           resultsTextBox.Text  = "rn2:  Entering AccessTheWebAsync.";

 

           HttpClient client = new HttpClient();

 

           resultsTextBox.Text  = "rn        Calling HttpClient.GetStringAsync.rn";

 

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

 

           DoIndependentWork();

 

           resultsTextBox.Text  = "rn4:  Back in startButton_Click.rn"  

               "       Task getStringTask is started.rn";

           string urlContents = await getStringTask;

 

           resultsTextBox.Text  = "rn5:  Back in AccessTheWebAsync."  

               "rn       Task getStringTask is complete."  

               "rn       Processing the return statement."  

               "rn       Exiting from AccessTheWebAsync.rn";

 

           return urlContents.Length;

       }

 

 

       void DoIndependentWork()

       {

           resultsTextBox.Text  = "rn3:  Entering DoIndependentWork.rn";

 

           resultsTextBox.Text  = "rn        Working . . . . . . .rn";

       }

运行结果:

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.rn"  

               "           Calling AccessTheWebAsync.rn";

 

           Task<int> getLengthTask = AccessTheWebAsync();

 

           resultsTextBox.Text  = "rnFOUR:  Back in startButton_Click.rn"  

               "           Task getLengthTask is started.rn"  

               "           About to await getLengthTask -- no caller to return to.rn";

 

           int contentLength = await getLengthTask;

 

           resultsTextBox.Text  = "rnSIX:   Back in startButton_Click.rn"  

               "           Task getLengthTask is finished.rn"  

               "           Result from AccessTheWebAsync is stored in contentLength.rn"  

               "           About to display contentLength and exit.rn";

 

           resultsTextBox.Text  =

               String.Format("rnLength of the downloaded string: {0}.rn", contentLength);

       }

 

       async Task<int> AccessTheWebAsync()

       {

           resultsTextBox.Text  = "rnTWO:   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  = "rn           Calling HttpClient.GetStringAsync.rn";

 

           // GetStringAsync returns a Task<string>. 

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

 

           resultsTextBox.Text  = "rnTHREE: Back in AccessTheWebAsync.rn"  

               "           Task getStringTask is started.";

 

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

 

           resultsTextBox.Text  =

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

 

           // Retrieve the website contents when task is complete.

           string urlContents = await getStringTask;

 

           resultsTextBox.Text  = "rnFIVE:  Back in AccessTheWebAsync."  

               "rn           Task getStringTask is complete."  

               "rn           Processing the return statement."  

               "rn           Exiting from AccessTheWebAsync.rn";

 

           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 创立职责

在本节中,首假使身体力行了什么创建三个职务。其关键利用了System.Threading.Tasks取名空间下的Task类。该类能够被实例化并且提供了一组静态方法,能够方便飞速的创设职分。

在下边实例代码中,分别延时了三种常见的职务创建立模型式,并且创办职责是能够钦点职分创建的选项,进而完毕最优的开创格局。

TaskCreationOptions中一共有7个枚举,枚举是足以选择|运算符组合定义的。其枚举如下表所示。

成员名称 说明
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

4.1 简介

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关键字创立了贰个状态机,类似于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。

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 1Task 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 运算符的四个或多少个相配项,但缺少 await 表明式不会造成编写翻译器错误。 尽管异步方法未使用 await 运算符标识悬挂点,则该办法将用作一道方法试行,不管异步修饰符怎样。编译器将为此类措施公布一个告诫。

Async 、async、Await 和 await 都是上下文关键字。 有关更加的多新闻和示范,请参见以下核心:

  • async
  • await

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构造函数同意传入叁个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

综上,我们在第3章中提过的异步编制程序模型和依照事件的异步编制程序模型,那几个形式使得获取结果越发轻松,传播也更自在,不过在开展七个异步操作结合的时候,还须要编写制定多量的代码。对于第1个问题.NET 4.0建议了多个新的关于异步操作的API。叫做职责并行库(Task Parallel Library 简称 TPL)。

1.9 互动运转职务

本节中至关心珍贵要介绍了多个法子的行使,二个是等待组中全部职分都实行实现的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的为主概念是职分。二个职分代表了八个异步操作,该操作可以动用各样艺术运维,能够采纳或不采纳独立线程运转。

小编水平有限,假设不当应接各位切磋指正!

本来想趁待业时期的时光读完《Multithreading with C# Cookbook Second Edition》那本书,而且享受做的连锁笔记;但是由于笔者前段时间专门的学业规划和肉体原因,大概近日都没有的时候间来更新那一个类别,无法实现几天一更。请大家多多饱含!可是笔者一定会将那个种类全体立异实现的!多谢大家的支撑!

一个任务能够有多样办法和其他任务组合起来。比如,能够何况执行多少个职责,等待全数职务完毕,然后运营贰个义务对从前全体的天职结果开展一些测算。TPL与在此之前的形式相比较,个中四个入眼优势是其有着用于组合职分的平价的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直接吃了

其次种是: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

地方结果呈现,父职必得得等待全体的子职务到位能力完结,可是看不出来他们是手拉手依旧异步推行的。因为从First Task和Sencod Task它们中间的周转时序上也看不出来他们是阿爸实施完了再进行的子职务,所以本人以为把父职责的时刻调长一点,那回自家让父职责推行10s

修改:

   return TaskMethod("First Task", 2);  →   return TaskMethod("First Task", 10);

结果如下

图片 23

那回显得的都以firstTask的Running状态,所以应该能自然老爹和儿子之间私下认可情形下也是异步试行的。因为父职分必供给等子职分全甘休本领产生。

 

 

本文由星彩网app下载发布于计算机编程,转载请注明出处:应用任务并行库,当中的十六线程_职分并行库

TAG标签: 星彩网app下载
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。