C# async await理解

C# async await 平时用的不多,每次用都要查一遍资料,看完就忘,特此记录一下。

以下为

用例

 void ClickMethod()
    {
        Log($"开始测试 ThreadId:{System.Threading.Thread.CurrentThread.ManagedThreadId}");
        TestAsync();
        Log($"TestAsync 主线程完成 ThreadId:{System.Threading.Thread.CurrentThread.ManagedThreadId}");
    }
    //使用async/await异步方法
    static async System.Threading.Tasks.Task TestAsync()
    {
        var async = MyTaskAsync();
        Log($"TestAsync await之前 ThreadId:{System.Threading.Thread.CurrentThread.ManagedThreadId}");
        await async;
        Log($"TestAsync await之后 ThreadId:{System.Threading.Thread.CurrentThread.ManagedThreadId}");
    }

    //真正开线程的方法
    static System.Threading.Tasks.Task<string> MyTaskAsync()
    {
        return System.Threading.Tasks.Task.Run(() =>
        {
            Log($"Task.Run MyTaskAsync 线程开始 ThreadId:{System.Threading.Thread.CurrentThread.ManagedThreadId}");
            System.Threading.Thread.Sleep(1000);
            Log($"Task.Run MyTaskAsync sleep 1秒 线程结束 ThreadId:{System.Threading.Thread.CurrentThread.ManagedThreadId}");
            return "task";
        });
    }
    //输出方法
    static void Log(string msg)
    {
        UnityEngine.Debug.Log(msg);
    }

TestAsync就是使用C#的async/await语法的异步方法

MyTaskAsync是真正开线程的方法,在里面Sleep了1秒

await之后的代码会等待MyTaskAsync的线程执行完毕,才会执行

Unity进行测试

把这段代码复制到Unity的脚本中执行。

第一步:我们执行了TestAsync,此时打开了一个线程,线程id是27

第二步:线程27开始Sleep,主线程1执行到await,离开了TestAsync方法 输出:主线程完成

第三步:1秒后,线程27执行结束,await等待也结束,执行await之后的代码 线程id是1

 在await之前的所有输出,我们就把await当做是return, 写过多线程的话,就能理解。Task.Run的lamda表达式里的都是在子线程3执行,主线程1不受影响,从await(return)出来之后继续执行。

异步方法做了什么?

反编译查看c#代码

先看一下dnspy C#是什么样的

 首先Output是我这些方法所在的类名,所以可以忽略。

<>c,只要这个类里有lamda表达式,编译器就会有这个类,<>9是这个<>c的单例(实例)

<MyTaskAsync>b__5_0就是我们写在Taski.Run Lamda的代码啦,一模一样的,相当于编译器帮我们从表达式变成了方法。这个不是重点,就这样吧。

来看TestAsync,可以说跟我们写的代码,不能说是一模一样,简直是毫不相干。

编译器生成了一个类型<TestAsync>d__6,是结构体(编译Release是结构体,Debug是类,算是代码优化),这里就直接是局部变量d__;

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

AsyncTaskMethodBuilder.Create() 建造者模式吧,创建了一个AsyncTaskMethodBuilder赋值给

d__.<>1__state = -1;

state赋值了-1

然后调用了上面builder的Start方法,并返回的builder里的Task

<TestAsync>d__6


[StructLayout(LayoutKind.Auto)]
private struct <TestAsync>d__6 : IAsyncStateMachine
{
	public int <>1__state;

	public AsyncTaskMethodBuilder <>t__builder;

	private TaskAwaiter<string> <>u__1;

	private void MoveNext()
	{
		int num = <>1__state;//前面有提到,一开始就是-1
		try
		{
			TaskAwaiter<string> awaiter;
            //前面有提到,一开始就是-1
			if (num != 0)
			{
				Task<string> task = MyTaskAsync();
				Log($"TestAsync await之前 ThreadId:{Thread.CurrentThread.ManagedThreadId}");
				awaiter = task.GetAwaiter();
				if (!awaiter.IsCompleted)
				{
                    //这里state变为0
					num = (<>1__state = 0);
					<>u__1 = awaiter;
                    //这个内部做了很多操作,大概就是等线程做完了,会再回调到MoveNext
                    //再次回调到MoveNext时,走的是下面else逻辑
					<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
					return;
				}
			}
			else
			{
				awaiter = <>u__1;
				<>u__1 = default(TaskAwaiter<string>);
				num = (<>1__state = -1);
			}
			awaiter.GetResult();
			Log($"TestAsync await之后 ThreadId:{Thread.CurrentThread.ManagedThreadId}");
		}
		catch (Exception exception)
		{
			<>1__state = -2;
			<>t__builder.SetException(exception);
			return;
		}
		<>1__state = -2;
		<>t__builder.SetResult();
	}

	void IAsyncStateMachine.MoveNext()
	{
		//ILSpy generated this explicit interface implementation from .override directive in MoveNext
		this.MoveNext();
	}

	[DebuggerHidden]
	private void SetStateMachine(IAsyncStateMachine stateMachine)
	{
		<>t__builder.SetStateMachine(stateMachine);
	}

	void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
	{
		//ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
		this.SetStateMachine(stateMachine);
	}
}

IAsyncStateMachine(微软文档:表示为异步方法生成的状态机。此类型仅供编译器使用。)

MoveNext(微软文档:将状态机移动到其下一个状态。)

public void MoveNext ();

SetMachineState(微软文档:使用堆分配的副本配置状态机。)

public void SetStateMachine (System.Runtime.CompilerServices.IAsyncStateMachine stateMachine);

await后的代码为什么在主线程执行?

await,等待子线程3执行完,为什么还是在主线程1呢?我们在前面可以看到,早在线程3执行完之前,主线程1就已经从TestAsync方法出来,做别的事了。怎么又回到线程1的?

同样的用例,放在Unity和WPF执行,输出是一样的,await之后的代码都是主线程。

然而在.net core执行则不同,await之后的代码线程id与Task.Run是一样的(我认为这更符合平时多线程编程的理解)。

这大概说明,不同平台实现方式不同。unity和WPF有图形界面,可能在状态机切换过程中回归了主线程。

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值