分享
 
 
 

参考资料

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: |||超大  

本文内容

概述编写异步方法异步程序中的控制流API 异步方法线程异步和等待返回类型和参数参考资料下载 Demo下载 Demo TPL 与 APM 和 EAP 结合(APM 和 EAP 这两个标准异步方式已经不能适应多核时代,但之前用这两种方式写的代码怎么办?——把它们改造一下,跟 TPL 结合)概述异步对可能起阻止作用的活动(例如,应用程序访问 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 2012 和 .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,你需要创建一个 TaskCompletionSource<TResult> 对象、执行异步操作,并在操作完成时,调用 SetResult、SetException、SetCanceled 方法,或调用这些方法之一的Try版本。 手动实现 TAP 方法时,需在所表示的异步操作完成时完成生成的任务。 例如:publicstaticTask<int> ReadTask(thisStream stream,byte[] buffer,intoffset,intcount,objectstate)

{

var tcs =newTaskCompletionSource<int>();

stream.BeginRead(buffer, offset, count, ar =>

{

try{ tcs.SetResult(stream.EndRead(ar)); }

catch(Exception exc) { tcs.SetException(exc); }

}, state);

returntcs.Task;

}

混合方法。你可能发现手动实现 TAP 模式、但将实现核心逻辑委托给编译器的这种方法很有用。 例如,当你想要验证编译器生成的异步方法之外的实参时,可能需要使用这种混合方法,以便异常可以转义到该方法的直接调用方而不是通过 System.Threading.Tasks.Task 对象被公开:publicTask<int> MethodAsync(stringinput)

{

if(input ==null)thrownewArgumentNullException("input");

returnMethodAsyncInternal(input);

}

 

PRivateasync Task<int> MethodAsyncInternal(stringinput)

{

 

// code that uses await goes here

 

returnvalue;

}

本文主要说明“使用编译器”方法。

编写异步方法C# 中async和await关键字是异步编程的核心。通过这两个关键字就可以轻松创建异步方法,几乎与创建同步方法一样。如下所示的 WPF 程序,布局文件上有个按钮和文本框:

privateasyncvoidStartButton_Click(objectsender, RoutedEventArgs e)

{

// Call and await separately.

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

//// You can do independent work here.

//int contentLength = await getLengthTask;

 

intcontentLength = 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 =newHttpClient();

 

// 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 awaitOperator 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.

stringurlContents = await getStringTask;

 

// The return statement specifies an integer result.

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

returnurlContents.Length;

}

 

 

voidDoIndependentWork()

{

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

}

执行结果:

Working . . . . . . .

 

Length of the downloaded string: 41609.

说明:

1,当程序访问网络时,无论你如何拖拽、最大化最小化、如何点击,UI 都不会失去响应;

2,“async Task<int> AccessTheWebAsync()”方法签名,有三点需要注意:1)有async修饰符;2)返回类型是Task或Task<int>。该方法是Task<int>,因为它返回的是链接内容的大小;3)方法名以 Async 结尾;

3,“stringurlContents = await getStringTask;”语句,有四点需要注意:1)AccessTheWebAsync方法直到getStringTask完成才能继续;2)同时,控制流返回到AccessTheWebAsync的调用者;3)getStringTask完成后,控制流才会恢复;4)之后,await操作符从getStringTask检索结果。

下面总结让一个示例成为异步方法的特征:

方法签名包含一个 async 修饰符。按照约定,异步方法的名称以“Async”后缀结尾。返回类型为下列类型之一:如果你的方法有 TResult 类型的返回语句,则为 Task<TResult>。如果你的方法没有返回语句,则为 Task。如果你编写的是异步事件处理程序,则为 Void(Visual Basic 中为 Sub)。方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。同时,将方法挂起,并且控件返回到方法的调用方。在异步方法中,可使用提供的关键字和类型来指示需要完成的操作,且编译器会完成其余操作,其中包括持续跟踪控件以挂起方法返回等待点时发生的情况。 一些常规流程(例如,循环和异常处理)在传统异步代码中处理起来可能很困难。 在异步方法中,元素的编写频率与同步解决方案相同且此问题得到解决。

异步程序中的控制流异步编程中最需弄清的是控制流是如何从方法移动到方法。

privateasyncvoidStartButton_Click(objectsender, 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";

 

intcontentLength = 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 =newHttpClient();

 

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";

stringurlContents = 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";

 

returnurlContents.Length;

}

 

 

voidDoIndependentWork()

{

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: BackinstartButton_Click.

Task getStringTask is started.

 

5: BackinAccessTheWebAsync.

Task getStringTask is complete.

Processing thereturnstatement.

Exiting from AccessTheWebAsync.

 

6: Length of the downloaded string: 41609.

再稍微复杂点:

privateasyncvoidstartButton_Click(objectsender, 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";

 

intcontentLength = 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 =

newHttpClient() { 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.

stringurlContents = 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";

 

returnurlContents.Length;

}

运行结果:

ONE: Entering startButton_Click.

Calling AccessTheWebAsync.

 

TWO: Entering AccessTheWebAsync.

Calling HttpClient.GetStringAsync.

 

THREE: BackinAccessTheWebAsync.

Task getStringTask is started.

Abouttoawait getStringTask andreturna Task<;int> to startButton_Click.

 

FOUR: BackinstartButton_Click.

Task getLengthTask is started.

Abouttoawait getLengthTask -- no callertoreturnto.

 

FIVE: BackinAccessTheWebAsync.

Task getStringTask is complete.

Processing thereturnstatement.

Exiting from AccessTheWebAsync.

 

SIX: BackinstartButton_Click.

Task getLengthTask is finished.

Result from AccessTheWebAsync is storedincontentLength.

Abouttodisplay contentLength andexit.

 

Length of the downloaded string: 41635.

API 异步方法

如何找到像 GetStringAsync 这样支持异步编程的方法。.NET Framework 4.5 包含使用 async 和 await 的许多成员,它们都已“Async”为后缀和 Task 或 Task<TResult> 的返回类型。例如,System.IO.Stream 类包含的方法 CopyToAsync、ReadAsync、WriteAsync 等方法以及同步方法 CopyTo、Read 和 Write。

线程异步方法旨在成为非阻止操作。异步方法中的 await 表达式在等待的任务正在运行时,不会阻止当前线程。相反,表达式在继续时,注册方法的其余部分并将控件返回到异步方法的调用方。

async 和 await 关键字不会导致创建其他线程。因为异步方法不会在其自身线程上运行,因此它不需要多线程。 只有当方法处于活动状态时,该方法将在当前同步上下文中运行并使用线程上的时间。 可以使用 Task.Run 将占用大量 CPU 的工作移到后台线程,但是后台线程不会帮助正在等待结果的进程变为可用状态。

对于异步编程而言,该基于异步的方法优于几乎每个用例中的现有方法。 具体而言,此方法比 BackgroundWorker 更适用于 IO 绑定的操作,因为此代码更简单且无需防止争用条件。 结合 Task.Run 使用时,异步编程比 BackgroundWorker 更适用于 CPU 绑定的操作,因为异步编程将运行代码的协调细节与 Task.Run 传输至线程池的工作区分开来。

异步和等待如果通过 async 修饰符指定某种方法为异步方法,则可以启用以下两个功能。

标记的异步方法可以使用 await 来指定悬挂点。await 运算符通知编译器异步方法只有直到等待的异步过程完成才能继续通过该点。 同时,控件返回至异步方法的调用方。 await 表达式中异步方法的挂起不能使该方法退出,并且 finally 块不会运行。标记的异步方法本身可以通过调用它的方法等待。异步方法通常包含 await 运算符的一个或多个匹配项,但缺少 await 表达式不会导致编译器错误。 如果异步方法未使用 await 运算符标记悬挂点,则该方法将作为同步方法执行,不管异步修饰符如何。编译器将为此类方法发布一个警告。

Async 、async、Await 和 await 都是上下文关键字。 有关更多信息和示例,请参见以下主题:

asyncawait

返回类型和参数.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;

// . . .

//Returnstatement specifies an integer result.

returnhours;

}

 

// CallstoTaskOfTResult_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 noreturnstatement.

}

 

// CallstoTask_MethodAsync

Task returnedTask = Task_MethodAsync();

await returnedTask;

// or,ina 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,它对应于 TaskIAsyncActionWithProgressIAsyncOperationWithProgress参考资料Microsoft Developer Network 基于任务的异步模式 (TAP)使用 Async 和 Await 的异步编程异步程序中的控制流 

下载 Demo

下载 Demo TPL 与 AMP 和 EAP 结合

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有