asp.net中的SynchronizationContext

https://msdn.microsoft.com/en-us/magazine/gg598924.aspx

指出了:
If multiple operations complete at once for the same application, AspNetSynchronizationContext will ensure that they execute one at a time. They may execute on any thread, but that thread will have the identity and culture of the original page.
When the WebClient object has downloaded the requested data, it will receive notification on a thread pool thread. This thread will raise DownloadDataCompleted in the captured context. The context will stay on the same thread but will ensure the event handler runs with the correct identity and culture.
1.AspNetSynchronizationContext 也是基于threadpool的,只是它帮忙恢复了identity和culture,但并不能保证异步delegate会在同一个thread上执行
2. 但是AspNetSynchronizationContext (每个app一个,被多个http request共享)是
Exclusive (Delegates Execute One at a Time)的。像winform和wpf一样,All delegates queued to the WindowsFormsSynchronizationContext are executed one at a time(这样就慢了); 但是顺序不保证(哪个httprequest的operation先完成就执行它的complete delegate),这点和Winform和WPF又不一样。
所以aspnet天生不适合大量使用webclient.DownloadDataAsync这种event based async, 就是因为complete event总要排队等AspNetSynchronizationContext 的档期,虽然可以进来避免长时间的Complete Event Handler, 但是排队等待总是慢的。到了aspnetcore时代,则变成了default SynchronizationContext (也就是null,也就是threadpool实现),可以很快地从thread pool里 QueueUserWorkItem一个thread来执行。
3.SynchronizationContext 是用于产生thread给异步的complete事件或者task的continue后续delegate的执行时用。当前层的xxxAsync方法或者aysnc fn()里的下一层的异步由下一层synccontext分配,而这个下一层synccontext可能性很多,比如backgroundworker,默认不设SynchronizationContext则固定用theadpool(default SynchronizationContext=null ), (理论上可以设SynchronizationContext来达到让backgroundworker在主线程上跑的效果)

if (SynchronizationContext.Current == null) {
  SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
}

如果用await关键字await 一个task,比如由task.factory.start起来的task, 则还要进一步看TaskScheduler.Current是TaskScheduler.Default(ThreadPoolTaskScheduler用Threadpool的机制 )还是基于SynchornazationContext的TaskSchduler。
比如如果await Task.Run<int>((Func<int>)this.fn); 因为Task.Run用的thread pool,所以fn在thread里执行(Task.Factory.StartNew(fn)也是默认用thread pool scheduler. ContinueWith默认也是thead pool.
但如果await Task.Factory.StartNew(fn, CancellationToken.None, TaskCreationOptions.None, scheduler: TaskScheduler.FromCurrentSynchronizationContext());
在WPF里的UI click事件里用这句话,则fn还是在Mainthread(UI thread)执行,在Console App则新起一个thread pool thread来运行。
总结,由于Winform和WPF的UI都可以利用上层的SynchornazationContext,所以他们的click方法可以直接写成async,里面await后的代码通过SynchornazationContext Post回到UI thread执行,SynchornazationContext还能传递下去给Task执行。但Console App就没有SynchornazationContext,所以Main方法不能直接写async,不能await Task,只能直接调用Task.Wait()在主线程上等Task在thread pool里执行完。