C#中使用 async await TaskCompletionSource<T>实现异步逻辑同步写

Task、async 和 await 是 C# 中用于处理异步编程的关键概念。它们一起构成了异步编程的基础。

Task

Task 是表示异步操作的抽象,它属于 System.Threading.Tasks 命名空间。Task 可以表示已经完成的任务、正在运行的任务或者尚未开始的任务。通过 Task,可以执行异步操作、并发操作,以及异步等待任务完成。

async 和 await

async 和 await 关键字是异步编程的基础构造,用于简化异步代码的编写。它们通常一起使用,使得编写异步代码更加直观、易读。

async

关键字用于定义一个异步方法。异步方法可以包含 await 操作符,并且在异步执行期间可以被挂起,而不会阻塞调用线程。

await

await 操作符用于等待异步操作完成,并返回异步操作的结果。在 async 方法中,await 会将控制权返回给调用者,而不会阻塞线程,从而提高了程序的响应性。

TaskCompletionSource

TaskCompletionSource 是用于创建和控制 Task 实例的一种灵活的方式。通常情况下,Task 表示一个异步操作的结果,而 TaskCompletionSource 则允许你手动控制异步操作的完成。
tcs.SetResult(42) 来设置异步操作的结果
tcs.SetCanceled() 异步取消
tcs.SetException() 异常
TaskCompletionSource 成为一种强大的工具,用于自定义异步操作的实现和控制。

例子 直接上结果

C#中使用 async await TaskCompletionSource<T>实现异步逻辑同步写_第1张图片
以往的代码实现上都是请求一个异步操作挂载一个回调方法
使用Task可以轻松实现异步操作同步写代码

public class LoginLogic
{
    public async static void Call()
    {
        C2S_Login c2S_Login = new C2S_Login()
        {
            name = "wukong",
            password = "123456",
        };

        S2C_Login s2C_Login = await TaskLogic.Instance.Call<S2C_Login>(c2S_Login);
        Debug.LogError($"接收消息, id: {s2C_Login.id}, time: {s2C_Login.time}, location: {s2C_Login.location}");
        //这里直接处理后续逻辑
    }
}

public class Test : MonoBehaviour
{
    void Start()
    {
        LoginLogic.Call();
    }
}

代码实现

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading.Tasks;
using System;
using System.Threading;

namespace Game
{
    public interface IResponse
    {
        public int id { get; set; }
    }

    public interface IRequest
    {
        public int id { get; set; }
    }

    public class C2S_Login : IRequest
    {
        public int id { get; set; }
        public string name;
        public string password;
    }

    public class S2C_Login : IResponse
    {
        public int id { get; set; }
        public int time;
        public string token;
        public string location;
    }

    public class RPCInfo : IDisposable
    {
        public int id;
        public TaskCompletionSource<IResponse> tcs;
        public CancellationTokenSource cts;

        public void SetResult(IResponse response)
        {
            tcs?.SetResult(response);
        }

        public void Dispose()
        {
            cts?.Dispose();
        }
    }

    public class TaskLogic
    {
        private static TaskLogic _instance;
        private int _id;
        private Dictionary<int, RPCInfo> _rpcs;

        //单位毫秒
        private const int RPC_TIMEOUT = 5000;

        public static TaskLogic Instance
        {
            get
            {
                if (_instance == null)
                    _instance = new TaskLogic();

                return _instance;
            }
        }

        public TaskLogic()
        {
            Init();
        }

        public string GetName()
        {
            return "TaskLogic";
        }

        public bool Init()
        {
            _rpcs = new Dictionary<int, RPCInfo>();
            return true;
        }

        private async void Send(IRequest request)
        {
            await Task.Run(async () =>
            {
                //这段代码只是用于模拟发送
                C2S_Login c2S_Login = request as C2S_Login;
                Debug.LogError($"发送消息, Name: {c2S_Login.name}, password: {c2S_Login.password}");

                //延迟一秒
                await Task.Delay(2000);

                //模拟接收
                S2C_Login s2C_Login = new S2C_Login()
                {
                    id = request.id,
                    time = DateTime.Now.Millisecond,
                    location = "北京",
                };

                Recv(s2C_Login);
            });
        }

        public async Task<T> Call<T>(IRequest request) where T : class, IResponse, new()
        {
            Interlocked.CompareExchange(ref _id, 0, int.MaxValue);
            request.id = Interlocked.Increment(ref _id);
            //1.模拟一下发消息, 延迟1秒后调用回复
            Send(request);
            //2.等待消息返回
            IResponse response = await WaitTask(request);
            return response as T;
        }

        private Task<IResponse> WaitTask(IRequest request)
        {
            TaskCompletionSource<IResponse> tcs = new TaskCompletionSource<IResponse>();
            CancellationTokenSource cts = new CancellationTokenSource();
            cts.CancelAfter(RPC_TIMEOUT);
            cts.Token.Register(() =>
            {
                Debug.LogError($"time out: {request}");
                _rpcs.Remove(request.id);
            });

            RPCInfo rpcInfo = new RPCInfo() { id = request.id, tcs = tcs, cts = cts };
            _rpcs.Add(request.id, rpcInfo);
            return rpcInfo.tcs.Task;
        }

        private void Recv(IResponse response)
        {
            RPCInfo rpcInfo;
            if (!_rpcs.TryGetValue(response.id, out rpcInfo))
                return;

            rpcInfo.Dispose();
            rpcInfo.SetResult(response);
            _rpcs.Remove(response.id);
        }

        public void UnInit()
        {
        }

        public void Update()
        {
        }
    }
}

你可能感兴趣的:(c#,unity)