Skip to content

Instantly share code, notes, and snippets.

@lightjiao
Last active April 19, 2024 11:34
Show Gist options
  • Save lightjiao/30bf01cfcdf7ed2bb989017e8e98e7b0 to your computer and use it in GitHub Desktop.
Save lightjiao/30bf01cfcdf7ed2bb989017e8e98e7b0 to your computer and use it in GitHub Desktop.
基于 UniTask 的支持并发的 Http 库
using Cysharp.Threading.Tasks;
using System.Collections.Generic;
using UnityEngine.LowLevel;
using UnityEngine.Networking;
namespace UniHttpLib
{
public class UniHttp
{
private static UniHttp m_Inst;
public static UniHttp Inst
{
get
{
if (m_Inst == null) m_Inst = new UniHttp();
return m_Inst;
}
}
private bool m_Init = false;
private UniHttp()
{
Init();
}
public void Init()
{
if (m_Init) return;
m_Init = true;
var uniHttpLoopSystem = new PlayerLoopSystem
{
type = typeof(UniHttp),
updateDelegate = Update,
};
var playerLoop =
#if UNITY_2019_3_OR_NEWER
PlayerLoop.GetCurrentPlayerLoop();
#else
PlayerLoop.GetDefaultPlayerLoop();
#endif
for (int i = 0; i < playerLoop.subSystemList.Length; i++)
{
if (playerLoop.subSystemList[i].type == typeof(UnityEngine.PlayerLoop.PreLateUpdate))
{
var subSystems = playerLoop.subSystemList[i].subSystemList;
var newSubsystem = new PlayerLoopSystem[subSystems.Length + 1];
subSystems.CopyTo(newSubsystem, 0);
newSubsystem[subSystems.Length] = uniHttpLoopSystem;
playerLoop.subSystemList[i].subSystemList = newSubsystem;
break;
}
}
PlayerLoop.SetPlayerLoop(playerLoop);
}
private class UniHttpReq
{
private static Stack<UniHttpReq> m_Pool = new Stack<UniHttpReq>();
public static UniHttpReq Acquire()
{
if (m_Pool.Count > 0) return m_Pool.Pop();
return new UniHttpReq();
}
public int UrlHashCode;
public UnityWebRequest Req;
public AutoResetUniTaskCompletionSource<object> Tcs;
public List<AutoResetUniTaskCompletionSource<object>> TcsArr;
private void Release()
{
TcsArr?.Clear();
TcsArr = null;
Tcs = null;
Req = null;
m_Pool.Push(this);
}
public void AppendTcs(AutoResetUniTaskCompletionSource<object> tcs)
{
if (TcsArr == null) TcsArr = new List<AutoResetUniTaskCompletionSource<object>>();
TcsArr.Add(tcs);
}
public void TrySetResult(object obj)
{
Tcs.TrySetResult(obj);
if (TcsArr != null)
{
foreach (var item in TcsArr)
{
item.TrySetResult(obj);
}
}
Release();
}
}
private Queue<UniHttpReq> m_Queue = new();
private Dictionary<int, UniHttpReq> m_Map = new();
private int m_GCTimer = 0;
private void Update()
{
if (m_Queue.Count == 0)
{
return;
}
var currentReq = m_Queue.Peek();
if (currentReq.Req.isDone)
{
m_Map.Remove(currentReq.UrlHashCode);
m_Queue.Dequeue();
currentReq.TrySetResult(currentReq.Req.downloadHandler.data);
}
m_GCTimer++;
if (m_GCTimer >= 1000)
{
m_GCTimer = 0;
m_Queue.TrimExcess();
m_Map.TrimExcess();
}
}
public UniTask<object> RequestAsync(string url)
{
var urlHashCode = url.GetHashCode();
var tcs = AutoResetUniTaskCompletionSource<object>.Create();
if (m_Map.ContainsKey(urlHashCode))
{
m_Map[urlHashCode].AppendTcs(tcs);
return tcs.Task;
}
var http = new UnityWebRequest();
http.url = url;
http.method = "GET";
http.downloadHandler = new DownloadHandlerBuffer();
http.SendWebRequest();
var uniReq = UniHttpReq.Acquire();
uniReq.UrlHashCode = urlHashCode;
uniReq.Req = http;
uniReq.Tcs = tcs;
m_Queue.Enqueue(uniReq);
m_Map[uniReq.UrlHashCode] = uniReq;
return tcs.Task;
}
}
}
@lightjiao
Copy link
Author

// 测试代码,用法用例
using Cysharp.Threading.Tasks;
using UniHttpLib;
using UnityEngine;

namespace UniHttpSample
{
    public class UniHttpSample : MonoBehaviour
    {
        public int Count = 100;

        private void Start()
        {
            StartAsync().Forget();
            StartAsync().Forget();
            StartAsync().Forget();
            StartAsync().Forget();
        }

        private async UniTaskVoid StartAsync(int Id = 0)
        {
            var obj = await UniHttp.Inst.RequestAsync("https://www.baidu.com" + Id);
            Debug.Log($"{Id}: Hello UniHttp:{obj}");
        }

        private int Uuid;
        private void Update()
        {
            if (Random.Range(0, 1f) < 0.01)
            {
                for (var i = 0; i < Random.Range(1, Count); i++)
                {
                    StartAsync(Uuid++).Forget();
                }
            }
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment