诺禾致源、异步函数async await在wpf都做了什么?

class Program
{
static async Task Main(string[] args)
{
System.Console.WriteLine( " T h r e a d I d i s T h r e a d : T h r e a d . C u r r e n t T h r e a d . M a n a g e d T h r e a d I d , I s T h r e a d P o o l : T h r e a d . C u r r e n t T h r e a d . I s T h r e a d P o o l T h r e a d " ) ; v a r r e s u l t = a w a i t E x a m p l e T a s k ( 2 ) ; S y s t e m . C o n s o l e . W r i t e L i n e ( "Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}"); var result = await ExampleTask(2); System.Console.WriteLine( "ThreadIdisThread:Thread.CurrentThread.ManagedThreadId,IsThreadPool:Thread.CurrentThread.IsThreadPoolThread");varresult=awaitExampleTask(2);System.Console.WriteLine(“Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}”);
System.Console.WriteLine(result);
Console.WriteLine(“Async Completed”);
}
private static async Task ExampleTask(int Second)
{
await Task.Delay(TimeSpan.FromSeconds(Second));
return $“It’s Async Completed in {Second} seconds”;
}
}
输出结果

Copy
Thread Id is Thread:1,Is Thread Pool:False
Thread Id is Thread:4,Is Thread Pool:True
It’s Async Completed in 2 seconds
Async Completed
假如这段代码在WPF运转,猜猜会输出啥?

Copy
private async void Async_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine( " T h r e a d I d i s T h r e a d : T h r e a d . C u r r e n t T h r e a d . M a n a g e d T h r e a d I d , I s T h r e a d P o o l : T h r e a d . C u r r e n t T h r e a d . I s T h r e a d P o o l T h r e a d " ) ; v a r r e s u l t = a w a i t E x a m p l e T a s k ( 2 ) ; D e b u g . W r i t e L i n e ( "Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}"); var result= await ExampleTask(2); Debug.WriteLine( "ThreadIdisThread:Thread.CurrentThread.ManagedThreadId,IsThreadPool:Thread.CurrentThread.IsThreadPoolThread");varresult=awaitExampleTask(2);Debug.WriteLine(“Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}”);
Debug.WriteLine(result);
Debug.WriteLine(“Async Completed”);
}
private async Task ExampleTask(int Second)
{
await Task.Delay(TimeSpan.FromSeconds(Second));
return $“It’s Async Completed in {Second} seconds”;
}
输出结果:

Copy
Thread Id is Thread:1,Is Thread Pool:False
Thread Id is Thread:1,Is Thread Pool:False
It’s Async Completed in 2 seconds
Async Completed
这时分你肯定是想说,小朋友,你能否有很多问号????,我们接下看下去

一.SynchronizationContext(同步上下文)#
首先我们晓得async await 异步函数实质是状态机,我们经过反编译工具dnspy,看看反编译的两段代码能否有不同之处:

控制台应用:

Copy
internal class Program
{
[DebuggerStepThrough]
private static Task Main(string[] args)
{
Program.
d__0
d__ = new Program.
d__0();

d__.args = args;

d__.<>t__builder = AsyncTaskMethodBuilder.Create();

d__.<>1__state = -1;

d__.<>t__builder.Start d__0>(ref
d__);
return
d__.<>t__builder.Task;
}

[DebuggerStepThrough]
private static Task ExampleTask(int Second)
{
	Program.d__1 d__ = new Program.d__1();
	d__.Second = Second;
	d__.<>t__builder = AsyncTaskMethodBuilder.Create();
	d__.<>1__state = -1;
	d__.<>t__builder.Start(ref d__);
	return d__.<>t__builder.Task;
}
[DebuggerStepThrough]
private static void 

(string[] args)
{
Program.Main(args).GetAwaiter().GetResult();
}
}
WPF:

Copy
public class MainWindow : Window, IComponentConnector
{
public MainWindow()
{
this.InitializeComponent();
}
[DebuggerStepThrough]
private void Async_Click(object sender, RoutedEventArgs e)
{
MainWindow.d__1 d__ = new MainWindow.d__1();
d__.<>4__this = this;
d__.sender = sender;
d__.e = e;
d__.<>t__builder = AsyncVoidMethodBuilder.Create();
d__.<>1__state = -1;
d__.<>t__builder.Start(ref d__);
}
[DebuggerStepThrough]
private Task ExampleTask(int Second)
{
MainWindow.d__3 d__ = new MainWindow.d__3();
d__.<>4__this = this;
d__.Second = Second;
d__.<>t__builder = AsyncTaskMethodBuilder.Create();
d__.<>1__state = -1;
d__.<>t__builder.Start(ref d__);
return d__.<>t__builder.Task;
}
[DebuggerNonUserCode]
[GeneratedCode(“PresentationBuildTasks”, “4.8.1.0”)]
public void InitializeComponent()
{
bool contentLoaded = this._contentLoaded;
if (!contentLoaded)
{
this._contentLoaded = true;
Uri resourceLocater = new Uri("/WpfApp1;component/mainwindow.xaml", UriKind.Relative);
Application.LoadComponent(this, resourceLocater);
}
}
private bool _contentLoaded;
}
我们能够看到完整是分歧的,没有任何区别,为什么编译器生成的代码是分歧的,却会产生不一样的结果,我们看看创立和启动状态机代码局部的完成:

Copy
public static AsyncVoidMethodBuilder Create()
{
SynchronizationContext synchronizationContext = SynchronizationContext.Current;
if (synchronizationContext != null)
{
synchronizationContext.OperationStarted();
}
return new AsyncVoidMethodBuilder
{
_synchronizationContext = synchronizationContext
};
}
[DebuggerStepThrough]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<[Nullable(0)] TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
AsyncMethodBuilderCore.Start(ref stateMachine);
}
[DebuggerStepThrough]
public static void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
if (stateMachine == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stateMachine);
}
Thread currentThread = Thread.CurrentThread;
Thread thread = currentThread;
ExecutionContext executionContext = currentThread._executionContext;
ExecutionContext executionContext2 = executionContext;
SynchronizationContext synchronizationContext = currentThread._synchronizationContext;
try
{
stateMachine.MoveNext();//状态机执行代码
}
finally
{
SynchronizationContext synchronizationContext2 = synchronizationContext;
Thread thread2 = thread;
if (synchronizationContext2 != thread2._synchronizationContext)
{
thread2._synchronizationContext = synchronizationContext2;
}
ExecutionContext executionContext3 = executionContext2;
ExecutionContext executionContext4 = thread2._executionContext;
if (executionContext3 != executionContext4)
{
ExecutionContext.RestoreChangedContextToThread(thread2, executionContext3, executionContext4);
}
}
}
在这里总结下:

创立状态机的Create函数经过SynchronizationContext.Current获取到当前同步执行上下文
启动状态机的Start函数之后经过MoveNext函数执行我们的异步办法
这里还有一个小提示,不论async函数里面有没有await,都会生成状态机,只是MoveNext函数执行同步办法,因而没await的状况下防止将函数标志为async,会损耗性能
同样的这里貌似没能获取到缘由,但是有个很关键的中央,就是Create函数为啥要获取当前同步执行上下文,之后我从MSDN找到关于SynchronizationContext
的引见,有兴味的朋友能够去阅读以下,以下是各个.NET框架运用的SynchronizationContext:

SynchronizationContext 默许
WindowsFormsSynchronizationContext WindowsForm
DispatcherSynchronizationContext WPF/Silverlight
AspNetSynchronizationContext ASP.NET
我们貌似曾经一步步接近真相了,接下来我们来看看DispatcherSynchronizationContext

二.DispatcherSynchronizationContext#
首先来看看DispatcherSynchronizationContext类的比拟关键的几个函数完成:

Copy
public DispatcherSynchronizationContext(Dispatcher dispatcher, DispatcherPriority priority)
{
if (dispatcher == null)
{
throw new ArgumentNullException(“dispatcher”);
}
Dispatcher.ValidatePriority(priority, “priority”);
_dispatcher = dispatcher;
_priority = priority;
SetWaitNotificationRequired();
}
//同步执行
public override void Send(SendOrPostCallback d, object state)
{
if (BaseCompatibilityPreferences.GetInlineDispatcherSynchronizationContextSend() && _dispatcher.CheckAccess())
{
_dispatcher.Invoke(DispatcherPriority.Send, d, state);
}
else
{
_dispatcher.Invoke(_priority, d, state);
}
}
//异步执行
public override void Post(SendOrPostCallback d, object state)
{
_dispatcher.BeginInvoke(_priority, d, state);
}
我们貌似看到了熟习的东西了,Send函数调用Dispatcher的Invoke函数,Post函数调用Dispatcher的BeginInvoke函数,那么能否WPF执行异步函数之后会调用这里的函数吗?我用dnspy停止了调试:

我经过调试之后发现,当等候执行完好个状态机的之后,也就是两秒后跳转到该Post函数,那么,我们能够将之前的WPF那段代码大约能够改写成如此:

Copy
private async void Async_Click(object sender, RoutedEventArgs e)
{
//async生成状态机的Create函数。获取到UI主线程的同步执行上下文
DispatcherSynchronizationContext synchronizationContext = (DispatcherSynchronizationContext)SynchronizationContext.Current;

//UI主线程执行
Debug.WriteLine($"Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}");

//开端在状态机的MoveNext执行该异步操作
var result= await ExampleTask(2);

//等候两秒,异步执行完成,再在同步上下文异步执行
synchronizationContext.Post((state) =>
{
     //模拟_dispatcher.BeginInvoke
     Debug.WriteLine($"Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}");
     Debug.WriteLine(result);
     Debug.WriteLine("Async Completed");  
 },"Post");           

}
输出结果:

Copy
Thread Id is Thread:1,Is Thread Pool:False
Thread Id is Thread:1,Is Thread Pool:False
It’s Async Completed in 2 seconds
Async Completed
也就是asyn担任生成状态机和执行状态机,await将代码分为两局部,一局部是异步执行状态机局部,一局部是异步执行完之后,经过之前拿到的DispatcherSynchronizationContext,再去异步执行接下来的局部。我们能够经过dnspy调试DispatcherSynchronizationContext的 _dispatcher字段的Thread属性,晓得Thread为UI主线程,而同步界面UI控件的时分,也就是经过Dispatcher的BeginInvoke函数去执行同步的

三.Task.ConfigureAwait#
Task有个ConfigureAwait办法,是能够设置能否对Task的awaiter的持续任务执行原始上下文,也就是为true时,是以一开端那个UI主线程的DispatcherSynchronizationContext执行Post办法,而为false,则以await那个Task里面的DispatcherSynchronizationContext执行Post办法,我们来考证下:

我们将代码改为以下:

Copy
private async void Async_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine( " T h r e a d I d i s T h r e a d : T h r e a d . C u r r e n t T h r e a d . M a n a g e d T h r e a d I d , I s T h r e a d P o o l : T h r e a d . C u r r e n t T h r e a d . I s T h r e a d P o o l T h r e a d " ) ; v a r r e s u l t = a w a i t E x a m p l e T a s k ( 2 ) . C o n f i g u r e A w a i t ( f a l s e ) ; D e b u g . W r i t e L i n e ( "Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}"); var result= await ExampleTask(2).ConfigureAwait(false); Debug.WriteLine( "ThreadIdisThread:Thread.CurrentThread.ManagedThreadId,IsThreadPool:Thread.CurrentThread.IsThreadPoolThread");varresult=awaitExampleTask(2).ConfigureAwait(false);Debug.WriteLine(“Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}”);
Debug.WriteLine(result);
Debug.WriteLine($“Async Completed”);
}
输出:

Copy
Thread Id is Thread:1,Is Thread Pool:False
Thread Id is Thread:4,Is Thread Pool:True
It’s Async Completed in 2 seconds
Async Completed
结果和控制台输出的一模一样,且经过dnspy断点调试照旧进入到DispatcherSynchronizationContext的Post办法,因而我们也能够证明我们上面的猜测,而且默许ConfigureAwait的参数是为true的,我们还能够将异步结果赋值给UI界面的Text block:

Copy
private async void Async_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine( " T h r e a d I d i s T h r e a d : T h r e a d . C u r r e n t T h r e a d . M a n a g e d T h r e a d I d , I s T h r e a d P o o l : T h r e a d . C u r r e n t T h r e a d . I s T h r e a d P o o l T h r e a d " ) ; v a r r e s u l t = a w a i t E x a m p l e T a s k ( 2 ) . C o n f i g u r e A w a i t ( f a l s e ) ; D e b u g . W r i t e L i n e ( "Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}"); var result= await ExampleTask(2).ConfigureAwait(false); Debug.WriteLine( "ThreadIdisThread:Thread.CurrentThread.ManagedThreadId,IsThreadPool:Thread.CurrentThread.IsThreadPoolThread");varresult=awaitExampleTask(2).ConfigureAwait(false);Debug.WriteLine(“Thread Id is Thread:{Thread.CurrentThread.ManagedThreadId},Is Thread Pool:{Thread.CurrentThread.IsThreadPoolThread}”);
this.txt.Text = result;//修正局部
Debug.WriteLine($“Async Completed”);
}
抛出异常:

Copy
调用线程无法访问此对象,由于另一个线程具有该对象

你可能感兴趣的:(诺禾致源、异步函数async await在wpf都做了什么?)