The Task: Events, Asynchronous Calls, Async and Await
The Task: Events, Asynchronous Calls, Async and Await
Almost any software application today will likely contain a long-running process. “Long-running” may be a relative term but in the Windows Runtime it is specifically anything that could take longer than 50ms to execute. That’s a fairly small window, and it means those operations will need to run concurrently to the main application thread. Concurrency is important in both client applications (to keep from blocking the UI) and server applications (to accommodate multiple simultaneous requests).
The new technology referred to as Visual Studio Asynchronous Programming provides a streamlined language syntax for asynchronous development. It does this by providing two new keywords: async and await. While these keywords may simplify asynchronous development, they can still be confusing to developers. There are a lot of materials out there but I thought it might help to take a very simple example and explore just what these keywords are and how they operate. In this post I’ll focus specifically on the .NET Framework 4.5 support. While they are also supported for Metro-style applications, the implementation is slightly different.
The Main Event
In the movie Mission Impossible II, the short-lived protagonist Dr. Nekhorvich says:
“…every search for a hero must begin with something every hero needs, a villain. So in a search for our hero, Bellerophon, we have created a more effective monster: Chimera.”
In the search for an elegant solution to asynchronous programming we must start with some of the rougher implementations that have plagued developers in the past.
The event-based pattern is probably one of the most well-known asynchronous patterns to .NET developers as it is prevalent throughout the base library. Let’s assume I have a method that multiplies two numbers and for some crazy reason (maybe I’m sending it over a 300 baud modem to my Commodore 64 to process the result on the 6502 chip … you know, using a bunch of ROR operations) it takes a bit longer to process than I’d like, so I want to make sure it executes asynchronously. The first thing I’ll do is create an event argument payload for the result:
public class MultiplyEventArgs : EventArgs
{
public int Result
{
get;
private set;
} public MultiplyEventArgs(int result)
{
Result = result;
}
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
Next, I’ll define an interface:
public interface IMultiplierEvent
{
event EventHandler<MultiplyEventArgs> MultiplyCompleted;
void MultiplyAsync(int a, int b);
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
Finally, I’ll implement the class that executes the operation asynchronous and fires the completed event when done.
public class MultiplierEvent : IMultiplierEvent
{ public event EventHandler<MultiplyEventArgs> MultiplyCompleted; private void RaiseCompleted(int result)
{ var handler = MultiplyCompleted;
if (handler != null)
{
handler(this, new MultiplyEventArgs(result));
}
} public void MultiplyAsync(int a, int b)
{
Task.Run(() => RaiseCompleted(a * b));
}
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
I can test this in a simple console program with a method call:
class Program
{
static void Main(string[] args)
{
var p = new Program(); p.DoEvent(); Console.ReadLine();
}
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
The implementation requires two methods, one to set it up and another to receive the completed event. Not bad, but you can imagine handling a chain of multiple events could get ugly quickly.
public void DoEvent()
{
Console.WriteLine("Firing as an event 2 * 3 ...");
IMultiplierEvent eventBased = new MultiplierEvent();
eventBased.MultiplyCompleted += eventBased_MultiplyCompleted;
eventBased.MultiplyAsync(2, 3);
} void eventBased_MultiplyCompleted(object sender, MultiplyEventArgs e)
{
Console.WriteLine("Event result: {0}", e.Result);
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
That’s the old world of events.
The Asynchronous Programming Model
Another popular asynchronous model is the Asynchronous Programming Model, or APM. The framework provides the IAsyncResult interface and you specify a pair of methods to Begin and End the operation. The first method always returns the IAsyncResult and the second method always takes the IAsyncResult and returns the result of the call.
In the implementation I create a nested class used to help maintain the state between the calls:
private class AsyncState
{
public Delegate MethodToCall { get; private set; }
public object State { get; private set; } public AsyncState(Delegate methodToCall, object state)
{
MethodToCall = methodToCall;
State = state;
}
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
Then I implement the methods. Notice that I’m casting the method call to a delegate and taking advantage of the built-in capability to invoke it asynchronously, then wrapping the end call to return the result.
public class MultiplierApm : IMultiplierApm
{
private class AsyncState [...] public IAsyncResult BeginMultiply(int a, int b, AsyncCallback callback, object state)
{
Func<int, int, int> multiply = Multiply;
var asyncState = new AsyncState(multiply, state);
return multiply.BeginInvoke(a, b, callback, asyncState);
} public int EndMultiply(IAsyncResult asyncResult)
{
var asyncState = (AsyncState)asyncResult.AsyncState;
var multiply = (Func<int, int, int>)asyncState.MethodToCall;
return multiply.EndInvoke(asyncResult);
} public int Multiply(int a, int b)
{
return a * b;
}
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
Now that I have an implementation, I can call it like this:
public void DoApm()
{
Console.WriteLine("Firing as APM 3 * 4 ...");
IMultiplierApm apmBased = new MultiplierApm();
apmBased.BeginMultiply(3, 4,
result =>
{
var value = apmBased.EndMultiply(result);
Console.WriteLine("Apm result: {0}", value);
}, null);
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
Note that I might have used two methods for the APM as well, I simplify chose to take a short cut but using a lambda expression instead.
Taking Asynchronous Operations to Task
With the new task library, setting up and calling asynchronous operations is far easier than the previous two approaches. First, I can use a single method signature and simplify specify the need for asynchronous operations by wrapping the result in a Task:
public interface IMultiplier
{
Task<int> Multiply(int a, int b);
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
For the implementation, I can easily use the built-in methods available in the library to spin off my thread:
public class Multiplier : IMultiplier
{
public Task<int> Multiply(int a, int b)
{
//return Task.Factory.StartNew(() => a * b);
return Task.Run(() => a * b); }
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
Finally, I can use the new keywords to make calling and waiting for the result easy. When I want to do something asynchronously without blocking the thread I’m on, I simply modify the method with the async keyword, then await the asynchronous operaiton. Again, it’s as simple as async to mark the method’s intention of spinning of a separate task to wait for, then await to launch the thread and receive the results in a single line of code.
public async void DoTask()
{
Console.WriteLine("Firing as task 4 * 5 ...");
IMultiplier taskBased = new Multiplier();
Console.WriteLine("Task result: {0}", await taskBased.Multiply(4, 5));
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
What happens here is that the current thread runs up until the await operation. There, the operation is spun off on a new thread. The main thread will wait for that thread to complete but will not block – it is not a synchronous operation. When the concurrent thread finishes what it is doing, it will drop the result back on the calling thread and allow me to interrogate the result. Notice that I can even nest the call inside of other operations – here the task must actually complete before it can pass the result to the string formatter which in turn sends the output to the console.
Simple interface design, simple implementation, and simple execution. I like it! But what do I do about those existing events and APM-based interfaces?
Wrapping Events
Fortunately, it’s possible to bundle up any asynchronous operation into a task. For this reason, I almost always declare my interfaces using Task. That way I can hide the underlying implementation. Let’s test this out. How can I take my IMultiplier interface and use it to call my MultiplierEvent class? The trick is to use a TaskCompletionSource. This special class allows me to perform an asynchronous operation using any pattern I prefer and then set a result when I’m done. The token will expose the event as a task that can be awaited. Here is a wrapper class that implements the simple interface:
public class MultiplierEventAsTask : IMultiplier
{
private IMultiplierEvent _event; public MultiplierEventAsTask()
{
_event = new MultiplierEvent();
} public Task<int> Multiply(int a, int b)
{
var tcs = new TaskCompletionSource<int>();
_event.MultiplyCompleted += (sender, args) =>
{
tcs.SetResult(args.Result);
};
_event.MultiplyAsync(a, b);
return tcs.Task;
}
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
I can then call it the same way I did the task-based implementation.
Wrapping the APM
Wrapping the APM model is even easier because the library provides a set of functions to convert existing operations. The FromAsync method will take most APM-style calls and turn them into tasks:
public class MultiplierApmAsTask : IMultiplier
{
private IMultiplierApm _apm; public MultiplierApmAsTask()
{
_apm = new MultiplierApm();
} public Task<int> Multiply(int a, int b)
{
return Task<int>.Factory.FromAsync(_apm.BeginMultiply,
_apm.EndMultiply, a, b, null);
}
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
Once again, the end result is the same simple interface despite a completely different implementation.
That’s a Wrap!
Using the Task we can now take various implementations and simplify them to a common, easy to use and understand interface. This little piece of code will execute each of the different implementations in order, waiting for the result but without blocking the main thread:
public async void DoAll()
{
// convert event to a task
Console.WriteLine("Firing all converted events in order ..."); IMultiplier taskFromEvent = new MultiplierEventAsTask();
IMultiplier taskFromApm = new MultiplierApmAsTask();
IMultiplier task = new Multiplier(); Console.WriteLine("2 * 3 = {0}", await taskFromEvent.Multiply(2, 3));
Console.WriteLine("4 * 5 = {0}", await taskFromApm.Multiply(4, 5));
Console.WriteLine("6 * 7 = {0}", await task.Multiply(6, 7));
}
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
There is a lot more you can do than simply await tasks. You can combine tasks, chain tasks, even wait for one or all tasks to complete before moving onto another task. Once you understand that async simply specifies the intention to do something asynchronously without blocking the main thread, and await simply launches a task and returns the result once that task is complete, you should be well on your way to simplifying your asynchronous development.
Download the sample project here.
(c) 2011-2012 Jeremy Likness.
The Task: Events, Asynchronous Calls, Async and Await的更多相关文章
- Thread、ThreadPool、Task、Parallel、Async和Await基本用法、区别以及弊端
多线程的操作在程序中也是比较常见的,比如开启一个线程执行一些比较耗时的操作(IO操作),而主线程继续执行当前操作,不会造成主线程阻塞.线程又分为前台线程和后台线程,区别是:整个程序必须要运行完前台线程 ...
- async 和 await 例子
/// <summary> /// C# 5.0 /// .net framework4.5 /// CLR4.0 /// 引入了async 和 await.这两个关键字可以让你更方便的写 ...
- C# 多线程(18):一篇文章就理解async和await
目录 前言 async await 从以往知识推导 创建异步任务 创建异步任务并返回Task 异步改同步 说说 await Task 说说 async Task 同步异步? Task封装异步任务 关于 ...
- 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 ...
- 异步方法的意义何在,Async和await以及Task的爱恨情仇,还有多线程那一家子。
前两天刚感受了下泛型接口的in和out,昨天就开始感受神奇的异步方法Async/await,当然顺路也看了眼多线程那几个.其实多线程异步相关的类单个用法和理解都不算困难,但是异步方法Async/awa ...
- 那些年我们一起追逐的多线程(Thread、ThreadPool、委托异步调用、Task/TaskFactory、Parallerl、async和await)
一. 背景 在刚接触开发的头几年里,说实话,根本不考虑多线程的这个问题,貌似那时候脑子里也有没有多线程的这个概念,所有的业务都是一个线程来处理,不考虑性能问题,当然也没有考虑多线程操作一条记录存在的并 ...
- C# Task中的Func, Action, Async与Await的使用
在说Asnc和Await之前,先说明一下Func和Action委托, Task任务的基础的用法 1. Func Func是一种委托,这是在3.5里面新增的,2.0里面我们使用委托是用Delegate, ...
- C# Thread、ThreadPool、Task、Invoke、BeginInvoke、async、await 汇总
本文将主要通过"同步调用"."异步调用"."异步回调"三个示例来讲解在用委托执行同一个"加法类"的时候的的区别和利弊. ...
- async和await用法(Task)
原文:async和await用法 要理解async和await的用法,首先要了解Task相关知识,这里不做说明,因为这不是本文的重点. 如果你已经对Task很了解,那么如何使用async和await, ...
随机推荐
- Swift 学习笔记1
最近在看Swift,努力在看相关的文档以及书籍,因为Swift3.0的更新,以及它开源了,所以打算写一些关于Swift的相关文章.让Swift能够更好的被我理解
- TweenMax动画库学习(二)
目录 TweenMax动画库学习(一) TweenMax动画库学习(二) TweenMax动画库学习(三) Tw ...
- 九度OJ1184二叉树
题目描述: 编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储).例如如下的先序遍历字符串:ABC##DE#G##F###其中“#”表示的是空格,空格字符代表空树 ...
- (转)互联网协议入门 ------ HTTP(1)
作者:阮一峰 原文:http://www.ruanyifeng.com/blog/2012/06/internet_protocol_suite_part_ii.html 我们每天使用互联网,你是否想 ...
- php 删除文件夹及文件
<?php function deleteDir($dir) { if (!$handle = @opendir($dir)) { return false; } while (false != ...
- MAC自带的lnmp
MAC自身带有apache,php. 1.启动apache服务 sudo apachectl start/restart/stop 2.查看php的版本 php -v 3.让apache支持ph ...
- Demo学习: CellDraw
CellDraw 学习TUniStringGrid,为什么有些设置在web模式下无效? case TUniBitBtn(Sender).Tag of : UniStringGrid1.ColWidth ...
- python--multiprocessing多进程总结
由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程.Python提供了非常好用的多进程包multiproces ...
- RepeatedDNASequences BestTime_to_Buy_and_SellStockIV
/** * @Author: weblee * @Email: likaiweb@163.com * @Blog: http://www.cnblogs.com/lkzf/ * @Time: 2015 ...
- about building flying sauser
download flying sauser: unzip flyingsaucer-master.zip cd flyingsaucer-master/ mvn install