FOUNDATION OF ASYNCHRONOUS PROGRAMMING
The async and await keywords are just a compiler feature. The compiler creates code by using the Task class. Instead of using the new keywords, you could get the same functionality with C# 4 and methods of the Task class; it's just not as convenient.
This section gives information about what the compiler does with the async and await keywords, an easy way to create an asynchronous method, how you can invoke multiple asynchronous methods in parallel, and how you can change a class that just offers the asynchronous pattern to use the new keywords.
Creating Tasks
Let's start with the synchronous method Greeting, which takes a while before returning a string (code file Foundations/Program.cs):
static string Greeting(string name)
{
Thread.Sleep(3000);
return string.Format("Hello, {0}", name);
}
To make such a method asynchronously, the method GreetingAsync is defined. The task-based asynchronous pattern specifies that an asynchronous method is named with the Async suffix and returns a task. GreetingAsync is defined to have the same input parameters as the Greeting method but returns Task<string>. Task<string>, which defines a task that returns a string in the future. A simple way to return a task is by using the Task.Run method. The generic version Task.Run<string>() creates a task that returns a string:
static Task<string> GreetingAsync(string name)
{
return Task.Run<string>(() =>
{
return Greeting(name);
});
}
Calling an Asynchronous Method
You can call this asynchronous method GreetingAsync by using the await keyword on the task that is returned. The await keyword requires the method to be declared with the async modifier. The code within this method does not continue before the GreetingAsync method is completed. However, the thread that started the CallerWithAsync method can be reused. This thread is not blocked:
private async static void CallerWithAsync()
{
string result = await GreetingAsync("Stephanie");
Console.WriteLine(result);
}
Instead of passing the result from the asynchronous method to a variable, you can also use the await keyword directly within parameters. Here, the result from the GreetingAsync method is awaited like in the previously code snippet, but this time the result is directly passed to the Console.WriteLine method:
private async static void CallerWithAsync2()
{
Console.WriteLine(await GreetingAsync("Stephanie"));
}
| Note |
|
In the next section you'll see what's driving this await keyword. Behind the scenes, continuation tasks are used.
Continuation with Tasks
GreetingAsync returns a Task<string> object. The Task object contains information about the task created, and allows waiting for its completion. The ContinueWith method of the Task class defines the code that should be invoked as soon as the task is finished. The delegate assigned to the ContinueWith method receives the completed task with its argument, which allows accessing the result from the task using the Result property:
private static void CallerWithContinuationTask()
{
Task<string> t1 = GreetingAsync("Stephanie");
t1.ContinueWith(t =>
{
string result = t.Result;
Console.WriteLine(result);
});
}
The compiler converts the await keyword by putting all the code that follows within the block of a ContinueWith method.
Synchronization Context
If you verify the thread that is used within the methods you will find that in both methods, CallerWithAsync and CallerWithContinuationTask, different threads are used during the lifetime of the methods. One thread is used to invoke the method GreetingAsync, and another thread takes action after the await keyword or within the code block in the ContinueWith method.
With a console application usually this is not an issue. However, you have to ensure that at least one foreground thread is still running before all background tasks that should be completed are finished. The sample application invokes Console.ReadLine to keep the main thread running until the return key is pressed.
With applications that are bound to a specific thread for some actions (e.g., with WPF applications, UI elements can only be accessed from the UI thread), this is an issue.
Using the async and await keywords you don't have to do any special actions to access the UI thread after an await completion. By default the generated code switches the thread to the thread that has the synchronization context. A WPF application sets a DispatcherSynchronizationContext, and a Windows Forms application sets a WindowsFormsSynchronizationContext. If the calling thread of the asynchronous method is assigned to the synchronization context, then with the continuous execution after the await, by default the same synchronization context is used. If the same synchronization context shouldn't be used, you must invoke the Task method ConfigureAwait(continueOnCapturedContext: false). An example that illustrates this usefulness is a WPF application in which the code that follows the await is not using any UI elements. In this case, it is faster to avoid the switch to the synchronization context.
Using Multiple Asynchronous Methods
Within an asynchronous method you can call not only one but multiple asynchronous methods. How you code this depends on whether the results from one asynchronous method are needed by another.
Calling Asynchronous Methods Sequentially
The await keyword can be used to call every asynchronous method. In cases where one method is dependent on the result of another method, this is very useful. Here, the second call to GreetingAsync is completely independent of the result of the first call to GreetingAsync. Thus, the complete method MultipleAsyncMethods could return the result faster if await is not used with every single method, as shown in the following example:
private async static void MultipleAsyncMethods()
{
string s1 = await GreetingAsync("Stephanie");
string s2 = await GreetingAsync("Matthias");
Console.WriteLine("Finished both methods.\n " +
"Result 1: {0}\n Result 2: {1}", s1, s2);
}
Using Combinators
If the asynchronous methods are not dependent on each other, it is a lot faster not to await on each separately, and instead assign the return of the asynchronous method to a Task variable. The GreetingAsync method returns Task<string>. Both these methods can now run in parallel. Combinators can help with this. A combinator accepts multiple parameters of the same type and returns a value of the same type. The passed parameters are "combined" to one. Task combinators accept multiple Task objects as parameter and return a Task.
The sample code invokes the Task.WhenAll combinator method that you can await to have both tasks finished:
private async static void MultipleAsyncMethodsWithCombinators1()
{
Task<string> t1 = GreetingAsync("Stephanie");
Task<string> t2 = GreetingAsync("Matthias");
await Task.WhenAll(t1, t2);
Console.WriteLine("Finished both methods.\n " +
"Result 1: {0}\n Result 2: {1}", t1.Result, t2.Result);
}
The Task class defines the WhenAll and WhenAny combinators. The Task returned from the WhenAll method is completed as soon as all tasks passed to the method are completed; the Task returned from the WhenAny method is completed as soon as one of the tasks passed to the method is completed.
The WhenAll method of the Task type defines several overloads. If all the tasks return the same type, an array of this type can be used for the result of the await. The GreetingAsync method returns a Task<string>, and awaiting for this method results in a string. Therefore, Task.WhenAll can be used to return a string array:
private async static void MultipleAsyncMethodsWithCombinators2()
{
Task<string> t1 = GreetingAsync("Stephanie");
Task<string> t2 = GreetingAsync("Matthias");
string[] result = await Task.WhenAll(t1, t2);
Console.WriteLine("Finished both methods.\n " +
"Result 1: {0}\n Result 2: {1}", result[0], result[1]);
}
Converting the Asynchronous Pattern
Not all classes from the .NET Framework introduced the new asynchronous method style with .NET 4.5. There are still many classes just offering the asynchronous pattern with BeginXXX and EndXXX methods and not task-based asynchronous methods as you will see when working with different classes from the framework.
First, let's create an asynchronous method from the previously-defined synchronous method Greeting with the help of a delegate. The Greeting method receives a string as parameter and returns a string, thus a variable of Func<string, string> delegate is used to reference this method. According to the asynchronous pattern, the BeginGreeting method receives a string parameter in addition to AsyncCallback and object parameters and returns IAsyncResult. The EndGreeting method returns the result from the Greeting method—a string—and receives an IAsyncResult parameter. With the implementation just the delegate is used to make the implementation asynchronously.
private static Func<string, string> greetingInvoker = Greeting; static IAsyncResult BeginGreeting(string name, AsyncCallback callback,
object state)
{
return greetingInvoker.BeginInvoke(name, callback, state);
} static string EndGreeting(IAsyncResult ar)
{
return greetingInvoker.EndInvoke(ar);
}
Now the BeginGreeting and EndGreeting methods are available, and these should be converted to use the async and await keywords to get the results. The TaskFactory class defines the FromAsync method that allows converting methods using the asynchronous pattern to the TAP.
With the sample code, the first generic parameter of the Task type, Task<string>, defines the return value from the method that is invoked. The generic parameter of the FromAsync method defines the input type of the method. In this case the input type is again of type string. With the parameters of the FromAsync method, the first two parameters are delegate types to pass the addresses of the BeginGreeting and EndGreeting methods. After these two parameters, the input parameters and the object state parameter follow. The object state is not used, so null is assigned to it. Because the FromAsync method returns a Task type, in the sample code Task<string>, an await can be used as shown:
private static async void ConvertingAsyncPattern()
{
string s = await Task<string>.Factory.FromAsync<string>(
BeginGreeting, EndGreeting, "Angela", null);
Console.WriteLine(s);
}
FOUNDATION OF ASYNCHRONOUS PROGRAMMING的更多相关文章
- Async/Await - Best Practices in Asynchronous Programming
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx Figure 1 Summary of Asynchronous Programming ...
- Async/Await - Best Practices in Asynchronous Programming z
These days there’s a wealth of information about the new async and await support in the Microsoft .N ...
- .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)
本文内容 异步编程类型 异步编程模型(APM) 参考资料 首先澄清,异步编程模式(Asynchronous Programming Patterns)与异步编程模型(Asynchronous Prog ...
- HttpWebRequest - Asynchronous Programming Model/Task.Factory.FromAsyc
Posted by Shiv Kumar on 23rd February, 2011 The Asynchronous Programming Model (or APM) has been aro ...
- Asynchronous programming with Tornado
Asynchronous programming can be tricky for beginners, therefore I think it’s useful to iron some bas ...
- Parallel Programming AND Asynchronous Programming
https://blogs.oracle.com/dave/ Java Memory Model...and the pragmatics of itAleksey Shipilevaleksey.s ...
- Asynchronous Programming Patterns
Asynchronous Programming Patterns The .NET Framework provides three patterns for performing asynchro ...
- C#的多线程——使用async和await来完成异步编程(Asynchronous Programming with async and await)
https://msdn.microsoft.com/zh-cn/library/mt674882.aspx 侵删 更新于:2015年6月20日 欲获得最新的Visual Studio 2017 RC ...
- Asynchronous programming with async and await (C#)
Asynchronous Programming with async and await (C#) | Microsoft Docs https://docs.microsoft.com/en-us ...
随机推荐
- CentOS 6.5 x64 安装Tomcat8 并配置两个Tomcat8
1.首先,安装tomcat的前提是已经配置好jdk环境变量,若没配好可以参考我的上一篇博文:CentOS 6.5 x64安装jdk8,当然也可以通过网络搜索安装步骤~~ 2.下载: 可以通过官网下载: ...
- NOI模拟赛(3.15) sequence(序列)
Description 小A有N个正整数,紧接着,他打算依次在黑板上写下这N个数.对于每一个数,他可以决定将这个数写在当前数列的最左边或最右边.现在他想知道,他写下的数列的可能的最长严格上升子序列(可 ...
- 【转】windows下nginx+mono+fastCGI部署asp.net网站
原文链接:http://www.cnblogs.com/amityat/archive/2011/08/23/2150153.html 1,什么是nginx 简介Nginx ("engine ...
- P1027 car的旅行路线
car的旅行路线 洛谷链接 这个题关键就是 如何把每个点表示出来,其实求出四个点的坐标后,只需要把这些点连接起来,用一遍folyed求出最短路径就好了. 代码: #include<cmath&g ...
- HDU4462稻草人
l问题描述:有一块N*N的玉米田(N<=50),给定K个(X,Y)的坐标位置(K<=10)和相应的覆盖范围,请问,至少在这K个位置中选择几个放置稻草人,能保证玉米田全被覆盖? #i ...
- POJ 3090 坐标系上的视线遮蔽问题
Description A lattice point (x, y) in the first quadrant (x and y are integers greater than or equal ...
- Asp.Net Thread is being Aborted
Asp.Net做的一个程序,通过JQuery的Ajax调用,程序执行的数据时间比较长,程序部署到服务器后执行一段时间后就弹出执行失败的对话框,日志记录的错误信息是“正在中止线程”. 查错过程: 1.根 ...
- 线程&线程池
线程 进程和线程: 进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位. 注意:两个都是过程 线程一个特点: 一个进程中,多个线程共享资源 线程和进程区 ...
- 最长不下降子序列 (O(nlogn)算法)
分析: 定义状态dp[i]表示长度为i的最长不下降子序列最大的那个数. 每次进来一个数直接找到dp数组第一个大于于它的数dp[x],并把dp[x - 1]修改成 那个数.就可以了 AC代码: # in ...
- HDU 1558
输入线段的两个短点,如果线段相交那么他们属于一个集合,查看第i条线段所在的集合有几条线段. 好久没码码了,总是各种蠢. 首先找出两条直线的方程,求解相交点的横坐标,然后看是不是在线段内部. 没有注意题 ...