前面些時(shí)候要研究串口通訊陡鹃,于是就接觸到了多線程方妖,期間呢分扎,筆者想當(dāng)然的把在線程里面使用了Unity 的 Text。然后就報(bào)錯(cuò)鲜棠,然后呢就遇見了 Loom,嗯培慌,設(shè)計(jì)的很巧妙豁陆,蠻好吃的!
所以吵护,我就把自家曾經(jīng)寫的 Timer,重構(gòu)了下献联,于是就有了本文,下面進(jìn)入正題:
異常&解決方案
Loom的設(shè)計(jì)
就是把操作Unity組件的邏輯塊使用 Action 包裹埋入到非主線程的上下文何址,但這個(gè)線程執(zhí)行到這個(gè)位置就把這些個(gè) Action 拋入MonoBehaviour的 Update中執(zhí)行他 里逆,實(shí)現(xiàn)的效果如下:
- 更優(yōu)的代碼的可讀性和邏輯連續(xù)性
- 更小范圍的數(shù)據(jù)可見性(閉包優(yōu)勢)
- 實(shí)時(shí)與 Unity 組件交互(閉包優(yōu)勢)
Timer的設(shè)計(jì)
只因?yàn)槎嗫戳艘谎郏虐l(fā)現(xiàn) Timer 的 TimerDriver 理念 原來跟這個(gè) Loom 是那么的相近:
都是利用委托Action 把邏輯塊插入其他邏輯塊的上下文用爪,然后利用閉包的優(yōu)勢共享這個(gè)被插邏輯塊上下文的局部變量原押。
然后其實(shí)呢,執(zhí)行這個(gè) Action 的是另一個(gè)繼承了MonoBehaviour 的類偎血,在Timer中 我謂之:TimerDirver诸衔。
小結(jié)
綜上盯漂,這個(gè)Loom 帶來的 Unity多線程 炫酷體驗(yàn),只需要簡單的重構(gòu)笨农,俺家的 Tiemr 也必定兼并你的特色功能就缆,下面就是重構(gòu)大體思路
重構(gòu) Timer
- 剔除Timer 中 UnityEgine 相關(guān)的API : Time.realtimeSinceStartup 、Time.time谒亦。
- 將上述剔除的 CurrentTime 實(shí)際驅(qū)動(dòng) 放到 TimerDriver Update中竭宰。
- 為線程安全,對定時(shí)器鏈表 List<Timer> timers 各處上鎖。
- 如果在非主線程中TimerDriver初始化會(huì)報(bào)錯(cuò)份招,新增
Timer.IntializeDriver()
切揭,提供手動(dòng)初始化TimerDriver 的能力,在主線程初始化不會(huì)這樣麻煩锁摔。
應(yīng)用場景1-簡單的應(yīng)用
using UnityEngine;
using System.Threading.Tasks;
using QFramework.TimeExtend;
using Timer = QFramework.TimeExtend.Timer;
using UnityEngine.UI;
public class TestForTimer : MonoBehaviour
{
public Text text;
private void Awake()
{
Timer.IntializeDriver(); //首次初始化不能放在非主線程內(nèi)廓旬。
}
void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
this.FunctionA();
}
}
internal void FunctionA()
{
Task task = new Task(() =>
{
string _name = "CreateWithTimer ←";
Timer.AddTimer(0).OnCompleted(() =>
{
text.text = _name; //先演示異常
new GameObject(_name);
});
});
task.Start();
}
}
動(dòng)畫演示
Tips:
先嘗試在 Task 內(nèi)直接更新 Text組件數(shù)據(jù) ,失斝逞孕豹!
再嘗試 Timer 內(nèi)運(yùn)行,完美解決報(bào)錯(cuò)十气!
應(yīng)用場景2 - Http下載
參考:Unity技術(shù)博客 - 客戶端斷點(diǎn)續(xù)傳 - 簡書
using UnityEngine;
using Timer = QFramework.TimeExtend.Timer;
using System;
using System.IO;
using UnityEngine.UI;
public class TestDownload : MonoBehaviour
{
public string url = "http://localhost:8083/bigFile";
public string savePath = "";
public Text finish;
public Text update;
HttpDownLoader DownLoader;
void Awake()
{
savePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "TestDownLoad");
Timer.IntializeDriver();
Loom.Initialize();
DownLoader = new HttpDownLoader();
DownLoader.OnDownLoadCompleted.AddListener(() =>
{
this.finish.text = "下載完成励背!";
});
DownLoader.OnDownLoadUpdate.AddListener(v =>
{
this.update.text = string.Format("下載進(jìn)度:{0} %", (v * 100).ToString("f2"));
});
}
private void Start()
{
this.DownLoader.DownLoad(url, savePath);
}
private void OnDisable()
{
this.DownLoader.Close();
}
}
動(dòng)畫演示
Tips:
使用 Timer 或者 Loom 將 OnComplete 和OnUpdate 回調(diào)埋進(jìn)去,實(shí)現(xiàn)事件驅(qū)動(dòng)的進(jìn)度刷新和下載完成提示桦踊,無需額外的判斷椅野。
擴(kuò)展閱讀
- Unity多線程(Thread)和主線程(MainThread)交互使用類——Loom工具分享
- 重構(gòu)后的 Timer -GitHub
- Unity技術(shù)博客 - 客戶端斷點(diǎn)續(xù)傳 - 簡書
Unity非主進(jìn)程內(nèi)訪問Unity組件報(bào)錯(cuò)、怎么在其他進(jìn)程直接操作Unity組件籍胯、Unity多線程
可以做為 Loom 插件的課外知識竟闪,但不保證這個(gè)Timer 能夠合理的處理高并發(fā),畢竟筆者是個(gè)菜雞兒杖狼,尤其是多線程編程炼蛤。