当前位置: 首页 > news >正文

东莞建设局门户网站忻州网站建设求职简历

东莞建设局门户网站,忻州网站建设求职简历,网站制作电话多少,网站备案icp过期C#5.0作为第五个C#的重要版本#xff0c;将异步编程的易用度推向一个新的高峰。通过新增的async和await关键字#xff0c;几乎可以使用同编写同步代码一样的方式来编写异步代码。 本文将重点介绍下新版C#的异步特性以及部分其他方面的改进。同时也将介绍WinRT程序一些异步编…C#5.0作为第五个C#的重要版本将异步编程的易用度推向一个新的高峰。通过新增的async和await关键字几乎可以使用同编写同步代码一样的方式来编写异步代码。 本文将重点介绍下新版C#的异步特性以及部分其他方面的改进。同时也将介绍WinRT程序一些异步编程的内容。 C# async/await异步编程 写async异步编程这部分内容之前看了好多文章反复整理自己的思路尽力保证文章的正确性。尽管如此仍然可能存在错误请广大园友及时指出感谢感谢。 异步编程不是一个新鲜的话题最早期的C#版本也内建对异步编程的支持当然在颜值上无法与目前基于TAP使用async/await的异步编程相比。异步编程要解决的问题就是许多耗时的IO可能会阻塞线程导致CPU空转降低效率或者一个长时间的后台任务会阻塞用户界面。通过将耗时任务异步执行来使系统有更高的吞吐量或保持界面的响应能力。例如界面在加载一幅来自网络的图像时还可以及时响应用户进行的其他操作。 按前文惯例先上一张图通览一下TAP模式下异步编程的方方面面然后由异步编程的发展来讨论一下TAP异步模式。 图1 APM C# .NET最早出现的异步编程模式被称为APM(Asynchronous Programming Model)。这种模式主要由一对Begin/End开头的组成。BeginXXX方法用于启动一个耗时操作需要异步执行的代码段相应的调用EndXXX来结束BeginXXX方法开启的异步操作。BeginXXX方法和EndXXX方法之间的信息通过一个IAsyncResult对象来传递。这个对象是BeginXXX方法的返回值。如果直接调用EndXXX方法则将以阻塞的方式去等待异步操作完成。另一种更好的方法是在BeginXXX倒数第二个参数指定的回调函数中调用EndXXX方法这个回调函数将在异步操作完成时被触发回调函数的第二个参数即是EndXXX方法所需要的IAsyncResult对象。 .NET中一个典型的例子如System.Net命名空间中的HttpWebRequest类里的BeginGetResponse和EndGetResponse这对方法 IAsyncResult BeginGetResponse(AsyncCallback callback, object state) WebResponse EndGetResponse(IAsyncResult asyncResult) 由方法声明即可看出它们符合前述的模式。 APM使用简单明了虽然代码量稍多但也在合理范围之内。APM两个最大的缺点是1.不支持进度报告 2.不能方便的“取消”。 EAP 在C# .NET第二个版本中增加了一种新的异步编程模型EAP(Event-based Asynchronous Pattern)EAP模式的异步代码中典型特征是一个Async结尾的方法和Completed结尾的事件。XXXCompleted事件将在异步处理完成时被触发在事件的处理函数中可以操作异步方法的结果。往往在EAP代码中还会存在名为CancelAsync的方法用来取消异步操作以及一个ProgressChanged结尾的事件用来汇报操作进度。通过这种方式支持取消和进度汇报也是EAP比APM更有优势的地方。通过后文TAP的介绍你会发现EAP中取消机制没有可延续性并且不是很通用。 .NET2.0中新增的BackgroundWorker可以看作EAP模式的一个例子。另一个使用EAP的例子是被HttpClient所取代的WebClient类新代码应该使用HttpClient而不是WebClient。WebClient类中通过DownloadStringAsync方法开启一个异步任务并有DownloadStringCompleted事件供设置回调函数还能通过CancelAsync方法取消异步任务。 TAP async/await 从.NET4.0开始新增了一个名为TPL的库主要负责异步和并行操作的处理目标就是使异步和并发操作有个统一的操作界面。TPL库的核心是Task类有了Task几乎不用像之前版本的异步和并发那样去和Thread等底层类打交道作为使用者的我们只需要处理好TaskTask背后有一个名为的TaskScheduler的类来处理Task在Thread上的执行。可以这样说TaskScheduler和Task就是.NET4.0中异步和并发操作的基础也是我们写代码时不二的选择。 对于Task可以将其理解为一个包装委托对象通常就是Action或Func对象并执行的容器从Task对象的创建就可以看出 Action action  ()  Console.WriteLine(Hello World); Task task1  new Task(action);Funcobject, string func  name  Hello World  name; Taskstring task2  new Taskstring(func, hystar , CancellationToken.None,TaskCreationOptions.None );//接收object参数真蛋疼很不容易区分重载把参数都写上吧。 执行这个Task对象需要手动调用Start方法 task1.Start(); 这样task对象将在默认的TaskScheduler调度下去执行TaskScheduler使用线程池中的线程至于是新建还是使用已有线程这个对用户是完全透明的。还也可以通过重载函数的参数传入自定义的TaskScheduler。 关于TaskScheduler的调度推荐园子里这篇文章前半部分介绍了一些线程执行机制很值得一度。 当我们用new创建一个Task对象时创建的对象是Created状态调用Start方法后将变为WaitingToRun状态。至于什么时候开始执行进入Running状态由TaskScheduler控制。Task的创建执行还有一种“快捷方式”即Run方法 Task.Run(()  Console.WriteLine(Hello World)); var txt  await Taskstring.Run(()  Hello World); 这种方式创建的Task会直接进入WaitingToRun状态。 Task的其他状态还有RanToCompletionCanceled以及Faulted。在到达RanToCompletion状态时就可以获得TaskT类型任务的结果。如果Task在状态为Canceled的情况下结束会抛出 OperationCanceledException。如果以Faulted状态结束会抛出导致任务失败的异常。 Task同时服务于并发编程和异步编程在Jeffrey Richter的CLR via C#中分别称这两种模式为计算限制的异步操作和IO限制的异步操作仔细想想这称呼也很贴切这里主要讨论下Task和异步编程的相关的机制。其中最关键的一点就是Task是一个awaitable对象这是其可以用于异步编程的基础。除了Task还有很多类型也是awaitable的如ConfigureAwait方法返回的ConfiguredTaskAwaitable、WinRT平台中的IAsyncInfo这个后文有详细说明等。要成为一个awaitable类型需要符合哪些条件呢其实就一点其中有一个GetAwaiter()方法该方法返回一个awaiter。那什么是awaiter对象呢满足如下3点条件即可 实现INotifyCompletion或ICriticalNotifyCompletion接口 有bool类型的IsCompleted属性 有一个GetResult()来返回结果或是返回void awaitable和awaiter的关系正如IEnumerable和IEnumerator的关系一样。推而广之下面要介绍的async/await的幕后实现方式和处理yield语法糖的实现方式差不多。 Task类型的GetAwaiter()返回的awaiter是TaskAwaiter类型。这个TaskAwaiter很简单基本上就是刚刚满足上面介绍的awaiter的基本要求。类似于EAP当异步操作执行完毕后将通过OnCompleted参数设置的回调继续向下执行并可以由GetResult获取执行结果。 简要了解过Task再来看一下本节的重点 - async异步方法。async/await模式的异步也出来很久了相关文章一大片这里介绍下重点介绍下一些不容易理解和值得重点关注的点。我相信我曾经碰到的困惑也是很多人的遇到的困惑写出来和大家共同探讨。 语法糖 对async/await有了解的朋友都知道这两个关键字最终会被编译为.NET中和异步相关的状态机的代码。这一部分来具体看一下这些代码了解它们后我们可以更准确的去使用async/await同时也能理解这种模式下异常和取消是怎样完成的。 先来展示下用于分析反编译代码的例子一个控制台项目的代码这是能想到的展示异步方法最简单的例子了而且和实际项目中常用的代码结构也差不太多 //实体类 public class User {public int Id { get; set; }public string UserName { get; set; }  hystar;public string Email { get; set; } }class Program {static void Main(string[] args){var service  new Service(new Repository());var name  service.GetUserName(1).Result;Console.WriteLine(name);} }public class Service {private readonly Repository _repository;public Service(Repository repository){_repository  repository;}public async Taskstring GetUserName(int id){var name  await _repository.GetById(id);return name;} }public class Repository {private DbContext _dbContext;private DbSetUser _set;public Repository(){_dbContext  new DbContext();_set  _dbContext.SetUser();}public async Taskstring GetById(int id){ //IO...var user  await _set.FindAsync(id);return user.UserName;} } 注意控制台版本的示例代码中在Main函数中使用了task.Result来获取异步结果需要注意这是一种阻塞模式在除控制台之外的UI环境不要使用类似Result属性这样会阻塞的方法它们会导致UI线程死锁。而对于没有SynchronizationContext的控制台应用确是再合适不过了。对于没有返回值的Task可以使用Wait()方法等待其完成。 这里使用ILSpy去查看反编译后的代码而且注意要将ILSpy选项中的Decompile async methods (async/await)禁用如下图否则ILSpy会很智能将IL反编译为有async/await关键字的C#代码。另外我也尝试过Telerik JustDecompile等工具但是能完整展示反编译出的状态机的只有ILSpy。 图2 另外注意应该选择Release版本的代码去查看这是在一个Stackoverflow回答中看到的说是有啥不同具体也没仔细看这里知道选择Release版exe/dll反编译就好了。下面以Service类为例来看一下反编译后的代码 图3 通过图上的注释可以看到代码主要由两大部分构成Service类原有的代码和一个由编译器生成的状态机下面分别具体了解下它们都做了什么。依然是以图片加注释为主重要的部分会在图后给出文字说明。 图4 通过上图中的注释可以大致了解GetUserName方法编译后的样子。我们详细介绍下其中几个点首先是AsyncTaskMethodBuilderT我感觉很有必要列出其代码一看 为了篇幅关系这里删除了部分复杂的实现取而代之的是介绍方法作用的注释性文字对于简单的方法或是重要的方法保留了代码。 namespace System.Runtime.CompilerServices {public struct AsyncTaskMethodBuilderTResult{internal static readonly TaskTResult s_defaultResultTask  AsyncTaskCache.CreateCacheableTaskTResult(default(TResult));//这也是一个很重要的类AsyncTaskMethodBuilder将一些操作进一步交给AsynchronousMethodBuilderCore来完成private AsyncMethodBuilderCore m_coreState;private TaskTResult m_task;[__DynamicallyInvokable]public TaskTResult Task{[__DynamicallyInvokable]get{TaskTResult task  this.m_task;if (task  null){task  (this.m_task  new TaskTResult());}return task;}}private object ObjectIdForDebugger{get{return this.Task;}}[__DynamicallyInvokable]public static AsyncTaskMethodBuilderTResult Create(){return default(AsyncTaskMethodBuilderTResult);}//开始状态机的执行[__DynamicallyInvokable, DebuggerStepThrough, SecuritySafeCritical]public void StartTStateMachine(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine{if (stateMachine  null){throw new ArgumentNullException(stateMachine);}//保存当前ExecutionContext这是很重要的一步后文会具体介绍ExecutionContextSwitcher executionContextSwitcher  default(ExecutionContextSwitcher);RuntimeHelpers.PrepareConstrainedRegions();try{ExecutionContext.EstablishCopyOnWriteScope(ref executionContextSwitcher);stateMachine.MoveNext();}finally{executionContextSwitcher.Undo();}}[__DynamicallyInvokable]public void SetStateMachine(IAsyncStateMachine stateMachine){this.m_coreState.SetStateMachine(stateMachine);}[__DynamicallyInvokable]public void AwaitOnCompletedTAwaiter, TStateMachine(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine{try{AsyncMethodBuilderCore.MoveNextRunner runner  null;Action completionAction  this.m_coreState.GetCompletionAction(AsyncCausalityTracer.LoggingOn ? this.Task : null, ref runner);if (this.m_coreState.m_stateMachine  null){TaskTResult task  this.Task;this.m_coreState.PostBoxInitialization(stateMachine, runner, task);}awaiter.OnCompleted(completionAction);}catch (Exception arg_5C_0){AsyncMethodBuilderCore.ThrowAsync(arg_5C_0, null);}}[__DynamicallyInvokable, SecuritySafeCritical]public void AwaitUnsafeOnCompletedTAwaiter, TStateMachine(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine{try{AsyncMethodBuilderCore.MoveNextRunner runner  null;//这是整个方法乃至类中最重要的一部分//获取当前状态执行完毕后下一步的操作Action completionAction  this.m_coreState.GetCompletionAction(AsyncCausalityTracer.LoggingOn ? this.Task : null, ref runner);if (this.m_coreState.m_stateMachine  null){TaskTResult task  this.Task;this.m_coreState.PostBoxInitialization(stateMachine, runner, task);}//将下一步操作传递给awaiter对象实际进入下一步还是通过awaiter来进行的。awaiter.UnsafeOnCompleted(completionAction);}catch (Exception arg_5C_0){AsyncMethodBuilderCore.ThrowAsync(arg_5C_0, null);}}[__DynamicallyInvokable]public void SetResult(TResult result){//设置结果//通过Task上的方法来完成}internal void SetResult(TaskTResult completedTask){//设置结果调用上面的方法来完成            }public void SetException(Exception exception){//设置异常//通过Task上的方法来实现}internal void SetNotificationForWaitCompletion(bool enabled){this.Task.SetNotificationForWaitCompletion(enabled);}private TaskTResult GetTaskForResult(TResult result){//获取Task包装的结果}} } 状态机的几种状态如下 -1表示还未开始执行 -2执行结束可能是正常完成也可能遇到异常处理异常后结束 0~下一个状态。如0表示初始的-1之后的下一个状态1表示0后的下一状态以此类推。 上面的类中还出现了一个很重要的类型AsyncMethodBuilderCore简单的了解一下这个类型也很有必要。 namespace System.Runtime.CompilerServices {internal struct AsyncMethodBuilderCore{internal sealed class MoveNextRunner{private readonly ExecutionContext m_context;internal IAsyncStateMachine m_stateMachine;[SecurityCritical]private static ContextCallback s_invokeMoveNext;[SecurityCritical]internal MoveNextRunner(ExecutionContext context, IAsyncStateMachine stateMachine){this.m_context  context;this.m_stateMachine  stateMachine;}[SecuritySafeCritical]internal void Run(){//这个方法被包装为“继续执行”委托实际执行的代码//这个方法最终要的作用是给继续执行的代码设置正确的ExecutionContext}[SecurityCritical]private static void InvokeMoveNext(object stateMachine){((IAsyncStateMachine)stateMachine).MoveNext();}}private class ContinuationWrapper{internal readonly Action m_continuation;private readonly Action m_invokeAction;internal readonly Task m_innerTask;internal ContinuationWrapper(Action continuation, Action invokeAction, Task innerTask){if (innerTask  null){innerTask  AsyncMethodBuilderCore.TryGetContinuationTask(continuation);}this.m_continuation  continuation;this.m_innerTask  innerTask;this.m_invokeAction  invokeAction;}internal void Invoke(){this.m_invokeAction();}}internal IAsyncStateMachine m_stateMachine;internal Action m_defaultContextAction;public void SetStateMachine(IAsyncStateMachine stateMachine){}//上文提到的获取“继续执行”委托的方法//方法通过包装内部类MoveNextRunner的Run方法来实现[SecuritySafeCritical]internal Action GetCompletionAction(Task taskForTracing, ref AsyncMethodBuilderCore.MoveNextRunner runnerToInitialize){Debugger.NotifyOfCrossThreadDependency();ExecutionContext executionContext  ExecutionContext.FastCapture();Action action;AsyncMethodBuilderCore.MoveNextRunner moveNextRunner;if (executionContext ! null  executionContext.IsPreAllocatedDefault){action  this.m_defaultContextAction;if (action ! null){return action;}moveNextRunner  new AsyncMethodBuilderCore.MoveNextRunner(executionContext, this.m_stateMachine);action  new Action(moveNextRunner.Run);if (taskForTracing ! null){action  (this.m_defaultContextAction  this.OutputAsyncCausalityEvents(taskForTracing, action));}else{this.m_defaultContextAction  action;}}else{moveNextRunner  new AsyncMethodBuilderCore.MoveNextRunner(executionContext, this.m_stateMachine);action  new Action(moveNextRunner.Run);if (taskForTracing ! null){action  this.OutputAsyncCausalityEvents(taskForTracing, action);}}if (this.m_stateMachine  null){runnerToInitialize  moveNextRunner;}return action;}private Action OutputAsyncCausalityEvents(Task innerTask, Action continuation){}internal void PostBoxInitialization(IAsyncStateMachine stateMachine, AsyncMethodBuilderCore.MoveNextRunner runner, Task builtTask){//初始化AsyncMethodBuilderCore中的状态机变量。这里发生装箱操作。}internal static void ThrowAsync(Exception exception, SynchronizationContext targetContext){//将异常与SynchronizationContext相关联}internal static Action CreateContinuationWrapper(Action continuation, Action invokeAction, Task innerTask  null){return new Action(new AsyncMethodBuilderCore.ContinuationWrapper(continuation, invokeAction, innerTask).Invoke);}internal static Action TryGetStateMachineForDebugger(Action action){//获取用于调试目的的“继续执行”委托}internal static Task TryGetContinuationTask(Action action){//获取“继续执行”的Task}} } 总结来说AsyncTaskMethodBuilderT和AsyncMethodBuilderCore控制着状态机的执行主要是在正确的Context下调用MoveNext方法并在执行状态机的过程中负责正确的设置ExecutionContext和SynchronizationContext。 介绍了这么多基础构造你可能更关心原来的调用Repository的方法的代码去哪了它们在状态机的代码中。下面就来看一下状态机 图5 通过注释应该可以了解这个状态机的细节了。 简单的说一下这个struct优化。一开始状态机被作为struct对象放置在栈上对于await的工作已经完成不需要等待的情况将快速结束状态机这样状态机直接出栈效率高。如果await的工作需要等待则控制异步方法执行的AsyncTaskMethodBuilder再将状态机移动到堆中。因为这种情况下会发生Context切换在SynchronizationContext不为空的情况下如果状态机还在栈上则会导致很大的切换负担。 其实搞成一个状态机的目的主要还是考虑到可能存在多个await的情况。对于只有1个await的情况其实状态机的必要性不大几个if也就够了下面扩展下上面的例子看看有2个以上await1个和2个await的状态机都是使用if/else解决问题从3个起开始不同时编译器产生的代码首先是扩展后的C#代码以WPF应用为例 public partial class MainWindow : Window {public MainWindow(){InitializeComponent();}private async void Button_Click(object sender, RoutedEventArgs e){var userService  new Service();Debug.Write(Thread.CurrentThread.ManagedThreadId);var avatar  await userService.GetUserAvatarAsync(1);Debug.Write(Thread.CurrentThread.ManagedThreadId);//使用获取的avatar} }public class Service {private readonly Repository _repository;private readonly WebHepler _webHelpler;private readonly ImageLib _imgLib;public Service(){_repository  new Repository();_webHelpler  new WebHepler();_imgLib  new ImageLib();}public async Taskbyte[] GetUserAvatarAsync(int id){Debug.WriteLine(Service--  Thread.CurrentThread.ManagedThreadId);var user  await _repository.GetByIdAsync(id);Debug.WriteLine(Service--  Thread.CurrentThread.ManagedThreadId);var email  user.Email;var avatar  await _webHelpler.GetAvatarByEmailAsync(email);Debug.WriteLine(Service--  Thread.CurrentThread.ManagedThreadId);var thumbnail  await _imgLib.GetImgThumbnailAsync(avatar);return thumbnail;} }public class Repository {private readonly DbContext _dbContext;private readonly DbSetUser _set;public Repository(){//_dbContext  new DbContext();//_set  _dbContext.SetUser();}public async TaskUser GetByIdAsync(int id){Debug.WriteLine(Repo--  Thread.CurrentThread.ManagedThreadId);//IO...var user  await _set.FindAsync(id);Debug.WriteLine(Repo--  Thread.CurrentThread.ManagedThreadId);return user;} }public class WebHepler {private readonly HttpClient _httpClient;public WebHepler(){_httpClient  new HttpClient();}public async Taskbyte[] GetAvatarByEmailAsync(string email){Debug.WriteLine(Http--  Thread.CurrentThread.ManagedThreadId);var url  http://avater-service-sample/  email;var resp  await _httpClient.GetByteArrayAsync(url);Debug.WriteLine(Http--  Thread.CurrentThread.ManagedThreadId);return resp;} }public class ImageLib {public async Taskbyte[] GetImgThumbnailAsync(byte[] avatar){//模拟一个异步图像处理任务return await Task.Run(() {Task.Delay(500);return avatar;});} } 依然以Service类为例来分析await编译后的样子 Service中的GetUserAvatar方法中的3个await将把函数体分割为4个异步区间如下 图6 编译生成的代码最主要的不同是生成的状态机变了依旧是通过截图和注释来说一下这个新的状态机的执行情况方便对比注释将只标出与之前状态机不同的部分 图7 通过上面的分析async/await关键字背后的秘密已经清清楚楚。下面来说一下线程的问题。 线程 关于async/await模式线程的问题刚开始学习async/await那阵看到很多文章各种各样的说法一度让我很迷惑。 一种观点是很多国外同行的文章里说的async/await本身不创建线程。StackoverFlow上很多回答也明确说async/await这两个新增的关键字只是语法糖编译后的代码不新建线程这曾经一度给我造成了很大的困惑“不创建线程的话要异步还有啥用”。 后来看到一种观点是园友jesse2013博文中的一句话 await 不会开启新的线程当前线程会一直往下走直到遇到真正的Async方法比如说HttpClient.GetStringAsync这个方法的内部会用Task.Run或者Task.Factory.StartNew 去开启线程。也就是如果方法不是.NET为我们提供的Async方法我们需要自己创建Task才会真正的去创建线程。 这个这个观点应该是正确的可后来看了很多代码后感觉还不完全是这样毕竟一个被调用的async方法就会产生一个新的Task而这个新的Task可能去“开启一个新线程”。改造下上面的代码测试这个问题 public class Service {private readonly Repository _repository;public Service(Repository repository){_repository  repository;}public async Taskstring GetUserName(int id){Console.WriteLine(Thread.CurrentThread.ManagedThreadId);var name  await _repository.GetById(id);Console.WriteLine(Thread.CurrentThread.ManagedThreadId);return name;} }public class Repository {private DbContext _dbContext;private DbSetUser _set;public Repository(){_dbContext  new DbContext();_set  _dbContext.SetUser();}public async Taskstring GetById(int id){        //IO...var user  await _set.FindAsync(id);return user.UserName;} } 在控制台应用中执行这段代码会发现输出的两个线程Id是不相同的。 提示控制台引用程序没有SynchronizationContext在不恢复SynchronizationContext的情况下能更好的看出线程的变化。 到底情况是怎样的呢这里试着分析下我的想法 这里先阐释清“创建新线程”这个概念。我认为在这种情况下大家说的“创建新线程”可以被认为是与调用方法使用不同的线程这个线程可能是线程池已有的也可能是新建并被加入到线程池的线程。明确这给之后继续说线程问题。 首先肯定一点async/await关键字不会创建新线程是对的。如上文代码中所示async/await被编译为一个状态机的确不参与Task的创建实际新建Task的是被调用的异步方法。也就是说每调用一次异步方法每一个await都会产生一个新的Task这个Task会自动执行。前面说过Task由TaskScheduler安排执行一般都会在一个与调用线程不同的线程上执行。 为了把这个问题解释清楚假设调用异步方法的线程为A异步方法启动后在B线程执行。当B线程开始执行后A线程将交出控制权。异步方法执行结束后后续代码await后面的代码将在B线程上使用A线程的ExecutionContext和SynchronizationContext默认情况继续执行。 注意这个A线程到B线程控制权的转换正是async异步模式的精髓之一。在WPF等这样的客户端环境这样做不会阻塞UI线程使界面不失去响应。在MVC这样的Web环境可以及时释放HTTP线程使Web服务器可以接收更多请求。毕竟B线程这种线程池中的线程成本更低。这样就是为什么既然也要花等待异步操作完成的时间还要另外使用异步方法的原因 - 及时释放调用线程让低成本的线程去处理耗时的任务。 最后当需要在发起执行的线程这里是A线程上继续进行处理时只要获得当时A线程的ExecutionContext和SynchronizationContext就可以了并在这些Context完成剩余操作即可。 如果后续还有其他await则会出现C线程D线程等。如B调用了C的话B的各种Context会被传递给C。当从异步方法返回后执行的线程变了但是Context没变。这样异步方法给我们的感觉就像是同步一般。这也就是async/await方法的精妙之处。 那个Task的ConfigureAwait方法又是做什么用的呢理解了上文就很好理解这个方法了。在异步方法返回时会发生线程切换默认情况下ConfigureAwait(true)时ExecutionContext和SynchronizationContext都会被传递。如果ConfigureAwait(false)则只有ExecutionContext会被传递SynchronizationContext不会被传递。在WPF等客户端程序UI部分应该使用默认设置让SynchronizationContext保持传递这样异步代码的后续代码才能正常操作UI。除此之外的其他情况如上面的Service类中都该使用ConfigureAwait(false)以放弃SynchronizationContext的传递来提高性能。 下面以图应该会对上面这段文字有更深的了解 吐槽一下本来是想用vs生成的时序图进行演示呢。结果发现vs2015取消这个功能了。手头也没有其他版本的vs。就用代码截图来掩饰这个线程变化过程吧。 首先是控制台程序的线程变化情况 图8 因为控制台应用没有SynchronizationContext所以可以清楚的看到线程的变化。 下面看看在WPF中类似流程执行的样子 图9 可以看到在默认情况下每个await后的异步代码返回到都回到UI线程即所有await的后继代码都使用UI线程的SynchronizationContext来执行。除了调用方法外其它所有的方法没有必要返回UI线程所以我们应该把除调用开始处即Button_Click方法外的所有异步调用都配置为ConfigureAwait(false)。 public partial class MainWindow : Window {public MainWindow(){InitializeComponent();}private async void Button_Click(object sender, RoutedEventArgs e){var userService  new Service();Debug.Write(Thread.CurrentThread.ManagedThreadId);var avatar  await userService.GetUserAvatarAsync(1);Debug.Write(Thread.CurrentThread.ManagedThreadId);//使用获取的avatar} }public class Service {private readonly Repository _repository;private readonly WebHepler _webHelpler;public Service(){_repository  new Repository();_webHelpler  new WebHepler();}public async Taskbyte[] GetUserAvatarAsync(int id){var user  await _repository.GetByIdAsync(id).ConfigureAwait(false);var email  user.Email;var avatar  await _webHelpler.GetAvatarByEmailAsync(email).ConfigureAwait(false);return avatar;} }public class Repository {private readonly DbContext _dbContext;private readonly DbSetUser _set;public Repository(){_dbContext  new DbContext();_set  _dbContext.SetUser();}public async TaskUser GetByIdAsync(int id){//IO...var user  await _set.FindAsync(id).ConfigureAwait(false);return user;} }public class WebHepler {private readonly HttpClient _httpClient;public WebHepler(){_httpClient  new HttpClient();}public async Taskbyte[] GetAvatarByEmailAsync(string email){var url  http://avater-service-sample/  email;var resp  await _httpClient.GetByteArrayAsync(url);return resp;} } 通过上面的图可以了解到有SynchronizationContext和没有SynchronizationContext环境的不同是否恢复SynchronizationContext的影响。对于ASP.NET环境虽然也有SynchronizationContext但实测线程切换的表现比较诡异实在无法具体分析但按照WPF的方式来配置异步肯定是对的。 其它资料据CLR via C#作者大神Jeffrey Richter在书中所说.NET这种以状态机实现异步的思想来自于其为.NET 4.0写的Power Threading库中的AsyncEnumerator类。可以将其作为一个参考来学习async异步方法的机制。 async异步编程中的取消和进度报告 由文章开始处的图1可知Task天生支持取消通过一个接收CancellationToken的重载创建的Task可以被通知取消。 var tokenSource  new CancellationTokenSource(); CancellationToken ct  tokenSource.Token; var task  Task.Run(()  Task.Delay(10000,ct), ct); tokenSource.Cancel(); 自然我们异步方法的取消也离不开CancellationToken方法就是给异步方法添加接收CancellationToken的重载如前文示例代码Service中的方法可以添加一个这样的重载支持取消 public async Taskbyte[] GetUserAvatarAsync(int id, CancellationToken ct) {... } async异步编程最大的一个特点就是传播性即如果有一个异步方法则所有调用这个方法的方法都应该是异步方法而不能有任何同步方法控制台应用Main函数中那种把异步转同步的方式除外。而通过CancellationToken实现的取消模式可以很好的适配这种传播性所需要做的就是把所有异步方法都添加支持CancellationToken的重载。之前的例子改造成支持取消后如下展示一部分 class Program {static void Main(string[] args){var tokenSource  new CancellationTokenSource();CancellationToken ct  tokenSource.Token;var userService  new Service();var avatar  userService.GetUserAvatarAsync(1,ct).Result;tokenSource.Cancel();Console.Read();} }public class Service {private readonly Repository _repository;private readonly WebHepler _webHelpler;public Service(){_repository  new Repository();_webHelpler  new WebHepler();}public async Taskbyte[] GetUserAvatarAsync(int id, CancellationToken ct){var user  await _repository.GetByIdAsync(id, ct);var email  user.Email;ct.ThrowIfCancellationRequested();var avatar  await _webHelpler.GetAvatarByEmailAsync(email, ct);return avatar;} } 注意ct.ThrowIfCancellationRequested()调用这是可以及时取消后续未完成代码的关键。当执行这个语句时如果ct被标记取消则这个语句抛出OperationCanceledException异常后续代码停止执行。 和取消机制一样新版的.NET也为进度通知提供了内置类型的支持。IProgressT和ProgressT就是为此而生。类型中的泛型参数T表示Progress的ProgressChanged事件订阅的处理函数的第二个参数的类型。扩展之前的例子把它改成支持进度报告的方法 class Program {static void Main(string[] args){var progress  new Progressint();progress.ProgressChanged  ( s, e )  {//e就是int类型的进度可以使用各种方式进行展示。};var userService  new Service();var avatar  userService.GetUserAvatarAsync(1,progress).Result;tokenSource.Cancel();Console.Read();} }public class Service {private readonly Repository _repository;private readonly WebHepler _webHelpler;public Service(){_repository  new Repository();_webHelpler  new WebHepler();}public async Taskbyte[] GetUserAvatarAsync(int id, IProgressint progress){var user  await _repository.GetByIdAsync(id, progress);//progress可以进一步传递但注意进度值要在合理范围内var email  user.Email;progress.Report(50);//报告进度var avatar  await _webHelpler.GetAvatarByEmailAsync(email, progress);progress.Report(100);return avatar;} } 可以看到在async异步模式下取消和进度都很容易使用。 以上介绍了拥有async/await支持的TAP异步编程。在编写新的异步代码时应该优先选用TAP模型而且新版的.NET库几乎给所有同步接口增加了这种可以通过async/await使用的异步接口。但往往项目中会存在一些使用APM或EAP模式的代码通过下面介绍的一些方法可以使用async/await的方式调用这些代码。 将BeginXXX/EndXXX的APM模式代码转为async异步方法只需要利用TaskFactory类的FromAsync方法即可我们以介绍APM时提到的HttpWebRequest为例 public TaskWebResponse GetResponseAsync(WebRequest client) {return TaskWebResponse.Factory.FromAsync(client.BeginGetResponse, client.EndGetResponse, null); } TaskFactory的FromAsync方法中使用TaskCompletionSourceT来构造Task对象。 封装EAP模式的代码要比APM麻烦一些我们需要手动构造TaskCompletionSource对象代码来自手打的。 WebClient client; Uri address; var tcs  new TaskCompletionSourcestring(); DownloadStringCompletedEventHandler hander  null; handler  (_, e) {client.DownloadStringCompleted - handler;if(e.Cancelled)tcs.TrySetCanceled();else if(e.Error ! null)tcs.TrySetException(e.Error);elsetcs.TrySetResult(e.Result); } client.DownloadStringCompleted  handler; client.DownloadStringAsync(address);return tcs.Task; 可以看到TaskCompletionSource提供了一种手动指定Task结果来构造Task的方式。 上面写了那么多真没有信息保证全部都是正确的。最后推荐3篇文章相信它们对理解async异步方法会有很大帮助本文的很多知识点也是来自这几篇文章 Understanding C# async / await (1) Compilation Understanding C# async / await (2) Awaitable-Awaiter Pattern Understanding C# async / await (3) Runtime Context WinRT 异步编程 C# WinRT是完全不同于.NET的一种框架目地就是把Windows的底层包装成API让各种语言都可以简单的调用。WinRT中对异步的实现也和.NET完全不同这一小节先看一下WinRT中异步机制的实现方法再来看一下怎样使用C#和.NET与WinRT中的异步API进行交互。 前文提到async异步编程中两个比较重要的对象是awaitable和awaiter。在WinRT中充当awaitable的是IAsyncInfo接口的对象具体使用中有如下4个实现IAsyncInfo接口的类型 IAsyncAction IAsyncActionWithProgressTProgress IAsyncOperationTResult IAsyncOperationWithProgressTResult, TProgress 由泛型参数可以看出Action和Operation结尾的两个类型不同之处在于IAsyncAction的GetResults方法返回void而IAsyncOperationTResult的GetResults方法返回一个对象。WithProgress结尾的类型在类似类型的基础上增加了进度报告功能它们内部定义了Progress事件用来执行进度变更时的处理函数。 Task和IAsyncInfo分别是对.NET和WinRT中异步任务的包装。它们的原理相同但具体实现有所不同。IAsyncInfo表示的任务的状态可以通过Status属性查询有如下几种和Task对照整理自MSDN Task状态 (TaskStatus类型) IAsyncInfo状态 (AsyncStatus类型) RanToCompletion Completed Faulted Error Canceled Canceled 所有其他值和已请求的取消 Canceled 所有其他值和未请求的取消 Started 另外获取异常的方式也不一样通过Task中的Exception属性可以直接得到.NET异常而IAsynInfo中错误是通过ErrorCode属性公开的一个HResult类型的错误码。当时用下文价绍的方法将IAsynInfo转为Task时HResult会被映射为.NET Exception。 之前我们说这些IAsyncXXX类型是awaitable的但为什么这些类型中没有GetAwaiter方法呢。真相是GetAwaiter被作为定义在.NET的程序集System.Runtime.WindowsRuntime.dll中的扩展方法因为基本上来说async/awati还是C#使用的关键字而C#主要以.NET为主。 这些扩展方法声明形如有多个重载下面是其中2个 public static TaskAwaiter GetAwaiterTResult(this IAsyncAction source); public static TaskAwaiterTResult GetAwaiterTResult, TProgress(this IAsyncOperationWithProgressTResult, TProgress source); 我们又见到了熟悉的TaskAwaiter。这个方法的实现其实也很简单以第一个重载为例 public static TaskAwaiter GetAwaiter(this IAsyncAction source) {return WindowsRuntimeSystemExtensions.AsTask(source).GetAwaiter(); } 可以看到就是通过task.GetAwaiter得到的TaskAwaiter对象。 这一系列扩展方法的背后又有一个更重要的扩展方法 - AsTask()。 AsTask方法有更多的重载其实现原理和前文介绍将EAP包装为async异步模式的代码差不多都是通过TaskCompletionSource来手工构造Task。下面展示的是一个最复杂的重载的实现 public static TaskTResult AsTaskTResult, TProgress(this IAsyncOperationWithProgressTResult, TProgress source, CancellationToken cancellationToken, IProgressTProgress progress) {if (source  null)throw new ArgumentNullException(source);TaskToAsyncOperationWithProgressAdapterTResult, TProgress withProgressAdapter  source as TaskToAsyncOperationWithProgressAdapterTResult, TProgress;if (withProgressAdapter ! null  !withProgressAdapter.CompletedSynchronously){TaskTResult task  withProgressAdapter.Task as TaskTResult;if (!task.IsCompleted){if (cancellationToken.CanBeCanceled  withProgressAdapter.CancelTokenSource ! null)WindowsRuntimeSystemExtensions.ConcatenateCancelTokens(cancellationToken, withProgressAdapter.CancelTokenSource, (Task) task);if (progress ! null)WindowsRuntimeSystemExtensions.ConcatenateProgressTResult, TProgress(source, progress);}return task;}switch (source.Status){case AsyncStatus.Completed:return Task.FromResultTResult(source.GetResults());case AsyncStatus.Canceled:return Task.FromCancellationTResult(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true));case AsyncStatus.Error:return Task.FromExceptionTResult(RestrictedErrorInfoHelper.AttachRestrictedErrorInfo(source.get_ErrorCode()));default:if (progress ! null)WindowsRuntimeSystemExtensions.ConcatenateProgressTResult, TProgress(source, progress);AsyncInfoToTaskBridgeTResult, TProgress infoToTaskBridge  new AsyncInfoToTaskBridgeTResult, TProgress();try{source.Completed  new AsyncOperationWithProgressCompletedHandlerTResult, TProgress(infoToTaskBridge.CompleteFromAsyncOperationWithProgress);infoToTaskBridge.RegisterForCancellation((IAsyncInfo) source, cancellationToken);}catch{if (Task.s_asyncDebuggingEnabled)Task.RemoveFromActiveTasks(infoToTaskBridge.Task.Id);throw;}return infoToTaskBridge.Task;} } 通过参数可以看到这个转换Task的过程支持调用方法传入的取消和进度报告。如果我们需要调用的WinRT异步方法的过程中支持取消和进度报告就不能直接await那个异步方法相当于调用了默认无参的AsTask的返回task上的GetAwaiter方法而是应该await显示调用的AsTask(可以传入CancellationToken及IProgress参数的重载上面那个)返回的task对象。这个可以见本小节末尾处的例子。 回头看一下上面给出的AsTask的实现。里面一个最终要的对象就是TaskToAsyncOperationWithProgressAdapterTResult, TProgress其可以由IAsyncOperationWithProgressTResult, TProgress直接转型而来。它也是IAsyncOperationWithProgressTResult, TProgress和Task之间的一个桥梁。这个类的工作主要由其父类TaskToAsyncInfoAdapterTCompletedHandler, TProgressHandler, TResult, TProgressInfo来完成。这个父类的实现就比较复杂了但道理都是相同的。有兴趣的同学自行查看其实现吧。 了解了原理最后来看一下代码示例WinRT中所有的IO相关的类中只提供异步方法示例因此也选择了这个使用最广泛的功能示例代码来源是某开源库具体是啥忘了有轻微改动 public async Taskstring ReadTextAsync(string filePath) {var text  string.Empty;using (var stream  await ReadFileAsync(filePath)){using (var reader  new StreamReader(stream)){text  await reader.ReadToEndAsyncThread();}}return text; } 有了async/await和上文介绍的扩展方法的支持C#调用WinRT的异步接口和使用.NET中的异步接口一样的简单。 如果是需要传递取消和进度报告怎么办呢 public async Taskstring ReadTextAsync(string filePath, CancellationToken ct, IProgressint progress) {var text  string.Empty;try{using (var stream  await ReadFileAsync(filePath).AsTask(ct, progress)){using (var reader  new StreamReader(stream)){text  await reader.ReadToEndAsyncThread().AsTask(ct, progress);}}}catch(OperationCanceledException) {...}return text; } 代码的简洁程度让你感到震撼吧。而且得到Task对象后不但可以方便的配置取消和进度报告还能通过ConfigureAwait来配置SynchronizationContext的恢复。 不知道参数ct和progress怎么来的同学可以看上一小节的取消和异步部分。 除了由IAsyncInfo到Task的转换外还可以由Task/TaskT转为IAsyncAction/IAsyncOperationT。这个转换的主要作用是把C#写的代码封装为WinRT供其它语言调用。实现这个操作的AsAsyncAction/AsAsyncOperationT方法也是定义于上面提到的System.Runtime.WindowsRuntime.dll程序集中。以本文第一小节的Service类为例将其GetUserName方法改造成返回IAsyncOperationstring的方法如下 public class Service {private readonly Repository _repository;public Service(Repository repository){_repository  repository;}public IAsyncOperationstring GetUserName(int id){var nameAsync  _repository.GetByIdAsync(id).AsAsyncOperation();return nameAsync;} } 这两个扩展方法是用简单方便但有一点不足的就是不能支持Task中的取消和进度报告。要解决这个问题可以使用IAsyncInfo的Run方法来获得IAsynInfo对象。Run方法支持多种不同类型的委托对象作为参数比较复杂的一种可以支持取消和进度报告作为委托对象一般是lambda表达式的参数比如把上面的例子改成支持取消和进度报告后如下 public class Service {private readonly Repository _repository;public Service(Repository repository){_repository  repository;}private async Taskstring GetUserNameInternal(int id, ){var name  await _repository.GetByIdAsync(id, ct, progress);return name;}public IAsyncOperationstring GetUserName(int id, CancellationToken ct, IProgressint progress){var nameAsync  AsyncInfo.Run(async (ct, progress){ var name  await GetUserNameInternal(id, ct, progress);return name;};return nameAsync;} } 内幕这样就轻松的实现了将C#编写的代码作为WinRT组件的过程。从如下AsAsyncOperation和AsyncInfo.Run的反编译代码来看很难知道这个方法的实现细节毕竟它们都是和WinRT Native代码相关的部分。 public static IAsyncOperationTResult AsAsyncOperationTResult(this TaskTResult source) {return (IAsyncOperationTResult) null; }public static IAsyncAction Run(FuncCancellationToken, Task taskProvider) {return (IAsyncAction) null; } WinRT异步编程 C 微软对C进行了扩展一方面是为C实现类似C#中基于Task的线程管理方式另一方面让C(准确说是C/CX)可以实现与WinRT规范的的异步接口互操作。 这些扩展主要定义于ppltask.h中concurrency命名空间下。 concurrency::task 先来看一下和.NET Task基本等价的task类型。这也是微软C扩展中并发异步线程管理的核心类型之一。微软围绕concurrency::task的设计的一些方法与C#中的Task相关方法真的非常下。下面的表格对比了C#的Task与C中的concurrency::task。有C# Task基础的话对于concurrency::task很容易就能上手。 C# TaskC concurrency::task构造 方式1constructorconstructor构造 方式2Task.Factory.StartNew() 用于异步 - create_task() 构造 方式3 用于并行 - make_task() 返回task_handle和task_group等同用。 阻塞 - 等待完成task.Wait()task::wait()阻塞 - 等待获取结果GetAwaiter().GetResult()task::get()任务状态类型TaskStatusconcurrency::task_status并行 - 等待全部Task.WhenAll()concurrency::when_all并行 - 等待部分Task.WhenAny()concurrency::when_any异步 - 任务延续Task.ContinueWith()task::then()接着讨论一下本节的重点内容微软给C带来的异步支持。 普通异步 看过之前介绍C#异步的部分可以知道支持异步的系统无非就由以下以下几部分组成任务创建、任务延续、任务等待、取消、进度报告等。依次来看一下ppltask.h中支持这些部分的方法。 create_task方法可以将函数对象广义上的函数对象包含如lambda表达式在C11中也多用lambda表达式作为函数对象包装成task类对象。如上文所述定义在ppltask.h中位于concurrency命名空间下的task类和异步方法关系最密切。下面的代码示例了concurrency::task的创建。 taskint op1  create_task([]()   {        return 0;   }); 在C11中一般都使用auto直接表示一些复杂的类型让编译器去推断。例子中写出完整的类型可以让读者更好的理解方法的返回类型。 而类似于.NET Task中的ContinueWith方法的task::then方法基本使用如下 op1.then([](int v){        return 0;   }); 在C中由于没有类似C#中async/await关键字的支持所以后续任务不能像C#中那样直接跟在await ...语句后必须通过task::then方法来设置。 then方法也可以实现链式调用如 auto t  create_task([]()   {        //do something   }).then([](int v){        return 0;   }); 关于后续代码执行上下文的问题如果create_task方法接受的函数对象返回的是taskT或taskvoid则后续代码会在相同的线程上下文运行如果返回的是T或void则后续任务会在任意上下文运行。可以使用concurrency::task_continuation_context来更改这个设置。具体用法是将task_continuation_context传给task::then其中那些接受task_continuation_context类型参数的重载。如果参数值为concurrency::task_continuation_context::use_arbitrary则表示指定延续在后台线程上运行如果参数值为concurrency::task_continuation_context::use_current则表示指定延续在调用了task::then的线程上运行。如 auto t  create_task([]()   {        //do something   }).then([](int v){        //do something else;   },task_continuation_context::use_arbitrary());//then()中传入的代码将在后台线程执行相对于C#中配置ConfigAwait(false)。 对于取消和异步的支持将在下一小段进行介绍那里的实现方式同样可以应用到这一部分中。 使用create_task的方式创建task的方法只用于C内部对task的管理。如果是希望将异步作为WinRT组件发布需要使用下面介绍的create_async。 如果是纯C中处理多线程任务除了使用Windows中所提供的task还可以考虑C11标准库中的thread后者跨平台更好。后文会有一部分介绍C11的thread。如果是对C#的TPL模型很熟悉转到C使用ppltask.h中的task会发现模型一致性很高。 支持WinRT的异步 1. 提供WinRT标准的异步方法 通过create_async方法可以将函数转为异步函数即这个方法是返回IAsyncInfo对象的。通过这个方法可以将代码包装成WinRT中标准的异步方法供其它语言调用。被包装的代码一般是可调用对象在C11中一般都使用Lambda表达式。返回的IAsyncInfo的具体类型上文介绍的四种之一是有传入的参数决定的。 create_async的声明 templatetypename _Function __declspec(    noinline ) auto create_async(const _Function _Func) - decltype(ref new details::_AsyncTaskGeneratorThunk_Function(_Func)); 可以看到为了确定这个模板方法的返回类型使用了C11的decltype和位置返回类型等新特性。 通常情况下传入create_async的函数对象的方法体是一般的代码。还以把create_task方法的调用传入create_async接收的lambda表达式的方法体中create_task返回的concurrency::task也可以配置一系列的then()最终这些配置都将反应给最外部的create_async的包装。 下面的代码就是包装了最简单的过程代码 IAsyncOperationint^ op2  create_async([]()   {        return 0;   }); 也可以像上面说的包装一段create_task的代码把C内部的任务暴露给WinRT接口 IAsyncOperationint^ op3  create_async([](){     return create_task(KnownFolders::DocumentsLibrary-GetFileAsync(Dictionary.txt)).then([](StorageFile^ file)     {                 int wordNum  0;         // 获取单词数         return wordNum;     }; }); 通过create_async的重载也可以轻松的支持取消和进度报告。 扩展的C使用的异步模式与C# TPL使用的标记式取消模型一致但在使用上还是稍有不同在介绍这种模式之前先来说说取消延续的问题如下面的代码 auto t1  create_task([]() - int {     //取消任务     cancel_current_task(); });   auto t2  t1.then([](taskint t) {     try     {         int n  t.get();         wcout  L后续任务  endl;     }     catch (const task_canceled e)     {              } });   auto t3  t1.then([](int n) {     wcout  L后续任务  endl; }); 这个例子中可以看到我们可以在task内部方法中通过cancel_current_task()调用来取消当前的任务。如果t1被手动取消对于t1的两个后继任务t2和t3t2会被取消t3不会被取消。这是由于t2是基于值延续的延续而t3是基于任务的延续。 接下来的示例展示了C中 的标记式取消 cancellation_token_source cts; auto token  cts.get_token();   auto t  create_task([] {     bool moreToDo  true;     while (moreToDo)     { //是不是的检查是否取消被设置         if (is_task_cancellation_requested())         { //取消任务             cancel_current_task();         }         else          {             moreToDo  do_work();         }     } }, token).then([]{ // 延续任务 },token,concurrency::task_continuation_context::use_current);//传递取消标记接收取消标记的重载还需要延续上下文的参数   // 触发取消 cts.cancel();   t.wait(); 通过使用cancellation_token取消也可以传递到基于任务的延续。 上面演示的例子cancellation_token是在create_async方法内部定义的更常见的情况在create_async的工作方法参数中显示声明cancellation_token并传入到工作方法内这样IAsyncXXX上面的Cancel方法被调用取消标志也会被自动设置从而触发链式的标记性取消。 说起来很抽象可以参考下面的代码 IAsyncAction^ DoSomething(){ return create_async([](cancellation_token ct) { auto t  create_task([ct]() { // do something }); }); } 这样当DoSomething返回值IAsyncAction对象的Cancel方法被调用后ct被标记为取消任务t会在合适的时间被取消执行。 C的cancellation_token有一个更高级的功能其上可以设置回调函数当cts触发取消时token被标记为取消时会执行这个回调函数的代码。 cancellation_token_registration cookie; cookie  token.register_callback([e, token, cookie]() {     // 记录task被取消的日志等       // 还可以取消注册的回调     token.deregister_callback(cookie); }); 说完取消再来看一下进度报告。下面的例子基本是演示进度报告最简单的例子。 IAsyncOperationWithProgressint, double^ DoSometingWithProgressAsync(int input) {     return create_async([this, input](progress_reporterdouble reporter) - int     { auto results  input;   reporter.report(1); // do something reporter.report(50); // do something reporter.report(100.0);           return results;     }); } 我们将一个concurrency::progress_reporterT对象当作参数传入create_async接收的工作函数。然后就可以使用reporter的report方法来报告进度。返回的IAsyncOperationWithProgress类型可以使这个进度报告与WinRT中调用这个方法的代码协同工作。 2. 调用WinRT标准的异步方法 说了创建异步方法再来看看使用C调用WinRT的异步方法。由于C中没有async/await那样的异步模式所以最值得关心的就是如何所以当一个任务完成后需要手动传入剩余的代码来继续后续任务的执行这里需要用到task的then方法首先我们需要把IAsyncInfo转为task。其实上面的代码已经演示了这个用法 不同于C#中通过AsTask方法将IAsyncInfo等类型转为Task对象。C中是使用create_task的方法就是上面介绍的那个不同的重载来完成这个工作 auto createFileTadk create_task(folder-CreateFileAsync(aa.txt,CreationCollisionOption::ReplaceExisting)); 接着调用task的then方法设置后续执行 createFileTadk.then([this](StorageFile^ storageFileSample) {            String^ filenamestorageFileSample-Name;         }); 捕获异常方面不涉及WinRT的部分遵循C的异常捕获原则WinRT交互部分需要保证抛出的异常可以被WinRT识别处理。 除了使用ppltask.h中的扩展还可以使用WRL中的AsyncBase模板类来实现C对WiinRT异步的支持。但后者的代码过于晦涩就不再介绍了。 说回来和WinRT交互就好用的语言还是C#C可以用于实现纯算法部分即位于WinRT下方的部分只需要在必要的时候通过WinRT公开让C#可调用的接口。这样代码的编写效率和执行效率都很高。另外C#的应用商店程序支持本地编译也是大势所趋在WinRT之上使用C#或C/CX区别不大。 C 11 线程并发异步 C在沉寂多年之后终于在新版标准中迎来爆发其中标准内置的线程支持就是一个完全全新的特性。在之前版本的C中没有标准的线程库实现跨平台的线程操作一般都要借助于第三方的库。现在有了C11相同的操作线程的代码可以在不同的编译器上编译执行从而可以实现跨平台的线程操作。 C新标准中的线程异步等看起来和C#的机制非常的像不知道微软和C标准委员会谁“借鉴”的谁。 下面按线程并发中同步支持异步这样的顺序来逐个了解下C新标准中增加的这些特性。介绍方式以C#的等价机制做对比篇幅原因很多都是一个纲领作用介绍一笔带过根据需要大家自行查找相应的功能的具体使用方法。 线程 C11标准库中引入了std::thread作为抽象线程的类型。其很多操作和.NET中的Thread类似。 C 11C# std::threadThread创建constructorconstructor插入一个线程t.join()  t表示std::thread对象下同t.Join() t表示Thread对象下同分离线程t.detach()无获取线程idt.get_id()Thread.CurrentThread.ManagedThreadId线程休眠std::this_thread::sleep_for()Thread.Sleep() 一段简单的综合示例代码 int main() {std::thread t1([](int a){ std::this_thread::sleep_for(std::chrono::seconds(2)) }, 3);t1.join();t1.detach();return 0;     } 多线程 - 互斥 C11中内建了互斥机制可以让多个线程安全的访问同一个变量。几种机制总结如下可能并非完全一直但效果上很类似 C 11C#原子类型 atomic_type std::atomicT Interlocked内存栅栏memory_order_type MemoryBarrier线程本地存储thread_local ThreadStatic LocalDataStoreSlot ThreadLocalT 互斥 std::mutex std::timed_mutex std::recursive_mutex std::recursive_timed_mutex Mutex锁lock_guardTlock通知 condition_variable condition_variable_any (notify_one/notify_all) ManualResetEvent AutoResetEvent 初始化call_once  上面介绍的线程或多线程支持都是一些很底层的接口。针对异步操作C11还提供了一些高级接口其中具有代表性的对象就是std::future和std::async。 std::future和C#中的TaskAwaiter比较相似而std::async作用正如C#中使用async关键字标记的异步方法。在C11中通过std::async将一个可调用对象包装厂一个异步方法这个方法将返回一个std::future对象通过std::future可以得到异步方法的结果。 看一下这段代码来自qicosmos老师的博文就能明白上面所说 std::futureint f1  std::async(std::launch::async, [](){          return 8;       });    coutf1.get()endl; 关于C11异步方面的特性强烈推荐qicosmos老师的博文以及他编写的图书《深入应用C11代码优化与工程级应用》。 C# 方法调用方信息 新版本的C#提供了方便获取方法调用者信息的功能对于需要调试以及输出一些日志的情况很有用。这样我们不需要像之前那样在每个需要记录日志的地方硬编码下调用的方法名提高了代码的可读性。 提供这个新功能的是几个应用于参数的Attribute CallerFilePathAttribute 获得调用方法所在的源文件地址 CallerLineNumberAttribute 被调用代码的行号 CallerMemberNameAttribute 调用方法的名称 使用其简单只需要声明一个参数然后把这些Attribute加在参数前面在函数中取到的参数值就是我们想要的结果。一个简单的例子如下 static void Caller() {Called(); }static void Called([CallerMemberName] string memberName  ,[CallerFilePath] string sourceFilePath  ,[CallerLineNumber] int sourceLineNumber  0) {Console.WriteLine(memberName);Console.WriteLine(sourceFilePath);Console.WriteLine(sourceLineNumber); } 输出如下 Main C:\Users\...\ConsoleApplication1\Program.cs 31 还算是简单方便尤其对于输出日志来说。 C#5.0还对Lambda捕获闭包外变量进行了一些小优化这个在之前文章介绍Lambda时有介绍这里不再赘述。 C 调用方法信息 在C中就有宏来完成类似的功能。由于C可以兼容C所以在C11之前一般都用这种C兼容的方式来获得被调用方法的信息。新版的C对此进行了标准化增加了一个名为__func__的宏来完成这个功能。 需要注意的是和C#中类似功能获得调用方法名称不同这个__func__宏得到的是被调用方法即__func__所在方法的名称。个人感觉C中__func__更实用。仍然是一个简单的例子 void Called() {     std::cout  __func__  std::endl; }   void Caller() {     Called(); } 调用Caller()将输出Called。 C中实现这个宏的方式就是在编译过程中在每个方法体的最前面插入如下代码 static const char* __func__  Called; 了解这个之后你会感觉这个宏没有那么神秘了。 除了新被标准化的__func__在大部分C编译器中仍然可以使用__LINE__和__FILE__获取当前行号和所在文件。 预告 下篇文章将介绍C#6带来的新特性C#6中没有什么重量级的改进据说编译器好像有很大改动那个不了解就不说了不是一般用户能仔细研究的。编译前端和编译后端发展这么多年复杂程度接近操作系统了大都是一些语法糖而且糖的数量还不少。欢迎继续关注。 本文断断续续写了很久中间还出去玩了2周。有什么错误请指正。 转载于:https://www.cnblogs.com/lsxqw2004/p/4922374.html
http://www.huolong8.cn/news/34290/

相关文章:

  • 北京建设工程交易协会网站济南信息化网站
  • 黄石企业网站设计深圳网站建设找哪家公司
  • 通州微网站优化wordpress如何获取会员字段
  • 哪家公司的网站做得好网站规划与建设
  • 昆明cms建站模板粉末涂料做网站有用吗
  • 030159网站建设与维护h网站模版
  • 企业管理网站在哪些网站做外贸好
  • it网站设计培训建筑工程网格化管理实施方案
  • wap网站开发 php襄阳手机网站建设
  • 在与客户谈网页广告时如何让客户相信网站流量医疗器械注册
  • 做咖啡网站.htaccess wordpress cdn
  • 河南网站推广电话临淄百度信息网
  • 视频网站开发用什么服务器最近热点新闻事件
  • 建设网站一般要多久猎头公司怎么找客户
  • 天津做网站软件全屋定制设计指南
  • 中国常德合肥建站优化
  • 在百度怎样建网站公众号模板
  • 国家建设管理信息网站互联网营销成功案例
  • 网站建设的实验的结论体育设施建设网站
  • 网站续费话术外贸网站开发哪家好
  • 福田区住房和建设局地址官方网站专业建站商
  • 数据库端口 wordpress优化网站价格
  • 网页制作怎么上传到网站快站建站教程
  • 制图网站中国建设银行网站-个人客
  • 长沙建设公司网站济南比较好的网站开发公司
  • 青岛网站域名备案广东新闻联播
  • 建设一个官方网站的费用天猫网站运营
  • wordpress的登录地址修改番禺网站优化
  • 广州微网站建设信息那个网站做毕业设计
  • 直播网站建设目的友情链接交换标准