async异步方案

async异步方案

async 是c#后面出的异步解决方案,不同于 yield 的生产者消费者模式
async 是完整的异步方案,包括异常处理,返回值处理等
async 的实现原理是编译器处理 async ,封装成状态机,配合 await 操作符注入回调

async/await的标准写法

    //  一般要传入 cancelToken 
    //  正如事件监听器一样,异步开始时注册监听器,取消时要反注册监听器
    //  而async取消操作是由 CancellationToken 完成
    public async Task TestAsync(CancellationToken cancelToken)
    {
   
        try
        {
   

        }
        catch (Exception ex) when (!(ex is OperationCanceledException))
        {
   
            //  OperationCanceledException 是取消操作抛出的异常,一般不处理
            //  只需要在 finally 中清理资源即可,最外层也会忽略该异常,不会报错
        }
        finally
        {
   
            //  当异步正常完成或取消后,在这里清理资源
        }

        //  如果整个函数中没有任何 await 执行,则编译器会发出警告,可以加入下面这句来避免警告
        //  有返回值的可以用 await Task.FromResult(ret);
        await Task.CompletedTask;
    }

    //  调用方标准写法
    //  最外层由同步函数转异步函数,只能使用 async void
    CancellationTokenSource m_cancelTokenSource;
    public async void Execute()
    {
   
        //  创建一个可取消的源
        m_cancelTokenSource = new CancellationTokenSource();
        await TestAsync(cancelTokenSource.Token);
    }
    //  取消异步操作
    public void Cancel()
    {
   
        m_cancelTokenSource.Cancel();
    }

取消任务

  • 所有任务都接受1个 CancellationToken 参数用来取消,你的async或自定义任务设计时也要这样
    1个token可以被多个await使用

    	var cts = new CancellationTokenSource();
    	cancelButton.onClick.AddListener(() =>
    	{
         
    		cts.Cancel();
    	});
    	await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().WithCancellation(cts.Token);
    	await UniTask.DelayFrame(1000, cancellationToken: cts.Token);
    
  • 你还可以通过 MonoBehaviour 或 GameObject 对象扩展获得 token,该token在 MonoBehaviour 销毁时会自动调用取消

    	await UniTask.DelayFrame(1000, cancellationToken: this.GetCancellationTokenOnDestroy());
    
  • 取消的内部实现是抛出 OperationCanceledException,因此外部可以通过捕获该异常来处理取消逻辑
    参考下面的异常处理

超时处理

  • 正常处理方式

    	var cts = new CancellationTokenSource();
    	// unitask扩展了 CancelAfterSlim,因此unity中建议用 CancelAfterSlim,标准c#中用 CancelAfter 
    	// 使用 CancelAfterSlim 必须保存返回值,并在异步结束调用 dispose 来停止定时器
    	IDisposable timer = cts.CancelAfterSlim(TimeSpan.FromSeconds(5)); 
    
    	//	一般外部会传入一个 cancelToken,把2个token组合起来
    	//	其实我们可以直接用 cancelToken.CancelAfterSlim(1000) ,这样就不需要串联token,
    	//		但是有2个问题,所以放弃该方案
    	//			一是无法区分是超时还是取消,
    	//			二是超时取消后,cancelToken.IsCancellationRequested 将返回true,你别的地方不能用这个来判断是否已取消
    	//		注意不能直接用 cancelToken.CancelAfter,因为CancelAfter无法停止定时器
    	var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancelToken, cts.Token);
    	
    	try
    	{
         
    		await UnityWebRequest.Get("http://foo").SendWebRequest().WithCancellation(linkedTokenSource.Token);
    	}
    	catch (OperationCanceledException ex)
    	{
         
    		if (cancelToken.IsCancellationRequested)
    		{
         
    			UnityEngine.Debug.Log("Cancel.");
    		}
    		else 
    		{
         
    			UnityEngine.Debug.Log("Timeout");
    		}
    	}
    	finally
    	{
         
    		timer.dispose();		//	销毁定时器
    		linkedTokenSource.Dispose();
    		cts.Dispose();
    	}
    
  • unitask提供的简便处理方式(不建议用这个,不方便跟其它 cancelToken 串联,可以用自己写的 TimeoutTokenSource)

    • CancellationTokenSource 的设计初衷是不能重复使用,也就是只要调用过Cancel就表示结束,要用再创建
      而 unitask 扩展的 CancelAfterSlim 是设计为可重复使用的,为解决这个矛盾创建了 TimeoutController

    • 使用 TimeoutController 的好处
      可以反复使用,不用重新创建
      只需要的销毁的地方调用 timeoutController.Dispose() 就会触发取消,不用组合另一个 CancellationTokenSource

    • 缺点
      不好跟其它 Token 串联,因为构造函数需要的是 TokenSource,其实内部也只用到Token,不知道为什么要传入 TokenSource

    	TimeoutController timeoutController = new TimeoutController(); 
    	CancellationTokenSource  clickCancelSource = new CancellationTokenSource();
    	
    	//跟别的取消组合,注意这里需要 CancellationTokenSource ,而不是 Cance
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值