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 成为一种强大的工具,用于自定义异步操作的实现和控制。
例子 直接上结果
以往的代码实现上都是请求一个异步操作挂载一个回调方法
使用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()
{
}
}
}