【.net6第五章】Async/Await


Async/await

1.C#5 (.NET4.5) 引入的语法糖
2.C#7.1,Main入口也可以
3.C#8.0,可以使用异步流await foreach和可释放对象 await using

private async Task Test8()
{
    await foreach (var i in this.Sequence())
    {
        Console.WriteLine($"this is {i} foreach await");
    }
}

public async IAsyncEnumerable<int> Sequence()
{
    for (int i = 0; i < 20; i++)
    {
        await Task.Delay(100);
        yield return i;
    }
}

一、await/async用法

Async是用来修饰方法的。如果只是在方法上写一个async,会出现警告。
在这里插入图片描述

await 是在方法内部的。如果在方法内部写await,没有async会报错。
在这里插入图片描述
aysnc+awaite是配套使用。

在这里插入图片描述
我们做个demo来简单演示一遍async/await的执行流程。
在这里插入图片描述
TaskDemo方法是主线程和子线程并行,在执行碰到await的时,主线程会返回继续执行Test方法的逻辑。最后End awaite可能是主线程执行,也可能是其他线程执行。有async/await 如果没有返回值,就直接返回一个Task。
那么我们也可以用task进行接收
在这里插入图片描述
如果有返回值的:

//例如方法:
private static async Task<long> TaskDemo()
{
     ....//业务逻辑
     return 999999999;
}

 Task<long> t = TaskDemo();
 //如果访问Result,就相当于是同步方法!
 long lResult = t.Result;//访问result,阻塞式   主线程等待所有的任务完成,
 t.Wait();//等价于上一行,阻塞式--同步
 long lResult = await t;//非阻塞,

//可以直接使用
long result=await TaskDemo();   //异步非阻塞

总结:线程遇到awai就返回,不阻塞,是并发执行。做到控制顺序,其实就是以写同步方法的方式来完成多线程。

提示:在winform中,主线程执行进入到子方法中,遇到await返回后,如果await还有其他逻辑代码,后续的代码必须是主线程执行。主线程返回后等待执行主方法逻辑使用long lResult = t.Result接收结果,但是由于主线程在这里等待任务完成,就会出现线程死锁,相互等待主线程执行完成。解决:可以新开一个线程错开相互等待,就是再开一个线程讲子方法抱起来

二、底层实现

1.Async的底层实现

给方法加上Async后,在底层会生成一个状态机(状态机:通常指的是状态机模式(State Design Pattern)或有限状态机(Finite State Machine,FSM)。这是一种设计模式,用于管理对象的状态和状态之间的转换。在状态机模式中,一个对象可以根据其内部状态的变化而改变其行为。)。

2.在IL解析中:

C#代码:

public async void SimpleMethod()
{
}

1).建造这模式去构建:
在这里插入图片描述
2).开始执行:
在这里插入图片描述
3).整理线程的上下文
在这里插入图片描述

4).调用MoveNext()方法
在这里插入图片描述
5).movenext的执行方式:try{} 中没有逻辑是因为我们在定义方法时,只创建一个空的方法。如果执行有异常,把状态设置为-2,如果没有异常也重置为-2,然后调用SetResult(),把结果包裹成一个Task,如果有结果返回,会包裹成Task,这就是为什么我们在C#代码中可以返回一个Task或者Task的原因。

1.Async/Await的底层实现

C#代码:

public class AsyncAwait
{
    public async void SimpleMethod()
    {
        await this.CreateTask();
    }
    public Task CreateTask()
    {
        return Task.Run(() =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("In async Method");
        });
    }
}

在此加上awaite之后,我们再来看看其中的变化:
在这里插入图片描述
这上面是没有什么变化的,但是在MoveNext中,就不一样了
在这里插入图片描述
解读:
1.首先生成一个awaiter,如果没有执行结束Iscompleted,重置状态为0,然后重新定义变量u_1=awater,然后调用AwaitUnsafeOnCompleted()方法,然后return直接返回了,这就符合上面说的,主线程在子方法执行逻辑到await关键字,会返回执行主方法的逻辑。

2.AwaitUnsafeOnCompleted():就是在状态机外包一层传到AwaitUnsafeOnCompleted()方法中。
在这里插入图片描述

3.AwaitUnsafeOnCompleted():中间就不一一解读了,我们直接把IL中的代码拉出来注释带到代码上去。

internal static void AwaitUnsafeOnCompleted<TAwaiter>(ref TAwaiter awaiter, IAsyncStateMachineBox box) where TAwaiter : notnull, ICriticalNotifyCompletion
{
	if (default(TAwaiter) != null && awaiter is ITaskAwaiter)
	{
	  //1.主要是这一行,我们上面定义了 TAwaiter awaiter ,所以awaiter 不为null,第一个if条件成立,进入UnsafeOnCompletedInternal()方法。
		TaskAwaiter.UnsafeOnCompletedInternal(Unsafe.As<TAwaiter, TaskAwaiter>(ref awaiter).m_task, box, continueOnCapturedContext: true);
	}
	else if (default(TAwaiter) != null && awaiter is IConfiguredTaskAwaiter)
	{
		ref ConfiguredTaskAwaitable.ConfiguredTaskAwaiter reference = ref Unsafe.As<TAwaiter, ConfiguredTaskAwaitable.ConfiguredTaskAwaiter>(ref awaiter);
		TaskAwaiter.UnsafeOnCompletedInternal(reference.m_task, box, reference.m_continueOnCapturedContext);
	}
	else if (default(TAwaiter) != null && awaiter is IStateMachineBoxAwareAwaiter)
	{
		try
		{
			((IStateMachineBoxAwareAwaiter)(object)awaiter).AwaitUnsafeOnCompleted(box);
		}
		catch (Exception exception)
		{
			System.Threading.Tasks.Task.ThrowAsync(exception, null);
		}
	}
	else
	{
		try
		{
			awaiter.UnsafeOnCompleted(box.MoveNextAction);
		}
		catch (Exception exception2)
		{
			System.Threading.Tasks.Task.ThrowAsync(exception2, null);
		}
	}
}


//2.进入方法
internal static void UnsafeOnCompletedInternal(Task task, IAsyncStateMachineBox stateMachineBox, bool continueOnCapturedContext)
{
    //应用程序中未启用“TplEventSource”的日志记录,所以结果为false,进入else条件,执行UnsafeSetContinuationForAwait() 方法
	if (TplEventSource.Log.IsEnabled() || Task.s_asyncDebuggingEnabled)
	{
		task.SetContinuationForAwait(OutputWaitEtwEvents(task, stateMachineBox.MoveNextAction), continueOnCapturedContext, flowExecutionContext: false);
	}
	else
	{
		task.UnsafeSetContinuationForAwait(stateMachineBox, continueOnCapturedContext);
	}
}


//3.进入UnsafeSetContinuationForAwait方法
internal void UnsafeSetContinuationForAwait(IAsyncStateMachineBox stateMachineBox, bool continueOnCapturedContext)
{
	if (continueOnCapturedContext)
	{
		SynchronizationContext current = SynchronizationContext.Current;
		if (current != null && current.GetType() != typeof(SynchronizationContext))
		{
			SynchronizationContextAwaitTaskContinuation synchronizationContextAwaitTaskContinuation = new SynchronizationContextAwaitTaskContinuation(current, stateMachineBox.MoveNextAction, flowExecutionContext: false);
			if (!AddTaskContinuation(synchronizationContextAwaitTaskContinuation, addBeforeOthers: false))
			{
				synchronizationContextAwaitTaskContinuation.Run(this, canInlineContinuationTask: false);
			}
			return;
		}
		TaskScheduler internalCurrent = TaskScheduler.InternalCurrent;
		if (internalCurrent != null && internalCurrent != TaskScheduler.Default)
		{
			TaskSchedulerAwaitTaskContinuation taskSchedulerAwaitTaskContinuation = new TaskSchedulerAwaitTaskContinuation(internalCurrent, stateMachineBox.MoveNextAction, flowExecutionContext: false);
			if (!AddTaskContinuation(taskSchedulerAwaitTaskContinuation, addBeforeOthers: false))
			{
				taskSchedulerAwaitTaskContinuation.Run(this, canInlineContinuationTask: false);
			}
			return;
		}
	}
	if (!AddTaskContinuation(stateMachineBox, addBeforeOthers: false))
	{
	    //重点!!!开启一个新得线程
		ThreadPool.UnsafeQueueUserWorkItemInternal(stateMachineBox, preferLocal: true);
	}
}

最后又回到MoveNext中,执行SetResult()方法返回结果!!!!!!

总结

具体的价值是什么?
1.降低编程难度
2.提高了吞吐量,提供了请求的响应能力,降低了线程的使用数量
3.降低计算的时间,谈不上去提高性能
适合什么场景?
1.跟第三方交互的(非托管资源,经常有async版本):
2.数据库openAsync-Redis
3.Web请求-Api
4.文件读取

文章中如有差异,请指出,望共勉!!!!!!

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值