Task、async 和 await 是 C# 中用于处理异步编程的关键概念。它们一起构成了异步编程的基础。
Task 是表示异步操作的抽象,它属于 System.Threading.Tasks 命名空间。Task 可以表示已经完成的任务、正在运行的任务或者尚未开始的任务。通过 Task,可以执行异步操作、并发操作,以及异步等待任务完成。
async 和 await 关键字是异步编程的基础构造,用于简化异步代码的编写。它们通常一起使用,使得编写异步代码更加直观、易读。
关键字用于定义一个异步方法。异步方法可以包含 await 操作符,并且在异步执行期间可以被挂起,而不会阻塞调用线程。
await 操作符用于等待异步操作完成,并返回异步操作的结果。在 async 方法中,await 会将控制权返回给调用者,而不会阻塞线程,从而提高了程序的响应性。
TaskCompletionSource 是用于创建和控制 Task 实例的一种灵活的方式。通常情况下,Task 表示一个异步操作的结果,而 TaskCompletionSource 则允许你手动控制异步操作的完成。
tcs.SetResult(42) 来设置异步操作的结果
tcs.SetCanceled() 异步取消
tcs.SetException() 异常
TaskCompletionSource 成为一种强大的工具,用于自定义异步操作的实现和控制。
以往的代码实现上都是请求一个异步操作挂载一个回调方法
使用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()
{
}
}
}