工作之后在很多项目中遇到了async相关的编程知识,这一块笔者了解甚少,属于是实践之后又回来学习理论,有一些小的心得分享给大家同时也巩固自身的学习

异步

对于如下一段程序
{
A();
B();
}
同步方式:运行A -> 等待运行A -> 等待A运行完了运行B
异步方式:运行A -> 等待运行A -> 在等待的过程中去运行CDEFG… ->A运行完了运行B额

一、async 和 await

异步方法使用 async修饰。其返回值类型为Task或者void,其中Task表示异步操作。

等待一个异步操作的完成使用await,await只能在异步方法中使用。
await A; =》如果A还没有完成,那么就暂停程序的顺序执行,并将控制权返回给调用者,调用者去干别的事,直到异步操作完成。
当异步操作完成之后,await会自动恢复后续代码执行。

二、Task

Task代表一个异步操作,相当于是一个将要完成任务的句柄的存在。

为什么异步方法返回Task

同步方法可以直接返回结果,因为计算已经完成,而异步方法返回时,计算还没完成,没法直接给Int,此时此刻需要一个容器来表示这个操作还在进行中,将来存放计算结果,将来存放异常信息,提供等待,回调,取消等能力。而这个容器就是Task

同步方法
public int GetResult(){
	Thread.Sleep(1000); //等待一秒
	return 42; //一秒后返回
异步方法
public Task<int> GetResultAsync(){
	await Task.Delay(1000) //不阻塞线程,先立马返回给你一个Task,这1秒你先去干别的活
	return 42; //1秒后结果就放到task中
}

Task结构

概念上,task内部大概是这样

class Task<int>{
	int _result;
	bool _isCompleted;
	Exception _error;

public void Wait(){}

public int Result{get{...}}
	
}

三、并发调用

并发调用就是等待多个异步任务它们全部完成的方法。
主要是靠 Task.WhenAll 和 Task.WhenAny这俩兄弟来完成。

  • Task.WhenAll => 是C#中并发执行多个任务并等待它们全部完成的方法。
  • Task.WhenAny => 是C#中并发执行多个任务并等待其中任何一个任务完成的方法。

它们都接受一个Task数组作为参数。

四、取消令牌Cancellation Token

在异步操作中,可能会遇到需要取消任务的情况,C#提供了CancellationToken来支持取消任务的机制,通过传递取消令牌,可以在异步方法中处理取消请求,提高程序的灵活性和响应性。

而Cancellation Token 是一种协作机制,操作自己决定何时检查,如何退出。

// 1. 创建 CancellationTokenSource
using var cts = new CancellationTokenSource();

// 2. 获取 Token 传给要支持取消的操作
CancellationToken token = cts.Token;

// 3. 在异步/后台操作中检查取消
Task.Run(() => DoWork(token), token);  // 注意同时传入 token 给 Task.Run

// 4. 在适当的时候发出取消请求
cts.Cancel();

void DoWork(CancellationToken token)
{
    for (int i = 0; i < 100; i++)
    {
        // 方式一:检查 IsCancellationRequested(推荐非频繁操作)
        if (token.IsCancellationRequested)
        {
            // 清理资源后退出
            Console.WriteLine("检测到取消,退出");
            return;
        }
        
        // 方式二:调用 ThrowIfCancellationRequested(推荐用于需要抛出异常终止的地方)
        token.ThrowIfCancellationRequested();
        
        // 执行实际工作...
        Thread.Sleep(100);
    }
}

public async Task DownloadAsync(string url, CancellationToken token)
{
     var client = new HttpClient();
    // 支持取消的异步方法可以传入 token
    var response = await client.GetAsync(url, token);
    var data = await response.Content.ReadAsStringAsync();
    
    // 也可以在自己的循环中检查
    await foreach (var item in ProcessStreamAsync(token).WithCancellation(token))
    {
        // ...
    }
}

令牌底层结构

// 1. CancellationTokenSource - 信号的"发射器"
public class CancellationTokenSource : IDisposable
{
    private CancellationTokenCallback _callbacks;  // 回调链表
    private int _state;  // 0: None, 1: Cancelled, 2: Disposed
    
    public void Cancel()  // 发出取消信号
    public CancellationToken Token { get; }  // 获取"接收器"
    public static CancellationTokenSource CreateLinkedTokenSource(...)
}

// 2. CancellationToken - 信号的"接收器"(结构体,零开销)
public struct CancellationToken
{
    private CancellationTokenSource _source;  // 引用源
    public bool IsCancellationRequested { get; }  // 检查是否被取消
    public void ThrowIfCancellationRequested()  // 取消时抛异常
    public CancellationTokenRegistration Register(Action callback)  // 注册回调
}

// 3. OperationCanceledException - 约定的"终止信号"
public class OperationCanceledException : Exception
{
    public CancellationToken? CancellationToken { get; }
}

五、UniTask-Unity中的异步

UniTask是Unity版本的异步编程,比原生Task更快,更适合Unity中的异步操作。

没有UniTask的时候,我们往往使用协助程。

而UniTask帮助解决没有返回值,不能try-catch,处理GC问题。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐