Unity Webgl Jslib + Web Worker的方式實(shí)現(xiàn)多線程

????????最近做項(xiàng)目遇到了一個(gè)需求,進(jìn)入場景可能需要加載幾百張外部圖片,最開始使用協(xié)程在主線程中加載,發(fā)現(xiàn)加載很慢很卡,嚴(yán)重影響使用。但因?yàn)閡nity webgl是不支持C#的多線程的,在前端唯一能實(shí)現(xiàn)多線程的手段只有一個(gè)——Web Worker(https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API)。那么問題的解決思路就很明了了:如何在unity中調(diào)用JS的方法,開啟Web Worker,并在Web Worker線程獲得結(jié)果后能夠傳遞到主線程回調(diào)C#的方法。
????????首先,是C#調(diào)用JS創(chuàng)建Worker。這個(gè)unity官方文檔里說的就很明白,只要新建一個(gè)Jslib文件,在里面實(shí)現(xiàn)要調(diào)用的方法,然后在C#里面聲明相同的引用方法就好了:
具體參數(shù)可以根據(jù)自己業(yè)務(wù)需求確定。
????????上面代碼能夠創(chuàng)建Worker線程,但是問題來了onmessage方法的執(zhí)行結(jié)果必定是異步返回的,那么什么方式才能在得到結(jié)果時(shí)候告訴unity的C#端呢?其實(shí)一般編譯成的wasm的項(xiàng)目,調(diào)用的時(shí)候會(huì)有用dyncall library。我們只要知道回調(diào)的方法的引用或者說指針,就可以回調(diào)。具體代碼如下:
????????上面的代碼是假設(shè)說我們worker線程返回了一個(gè)fileName和文件的ArrayBuffer,我們使用_malloc手動(dòng)開辟內(nèi)存,把對應(yīng)的值塞進(jìn)去并獲得對應(yīng)的指針。然后調(diào)用Module.dynCall_viii來執(zhí)行PostMsg傳過來callback引用。
????????Module.dynCall后邊的viii指的是回調(diào)函數(shù)的簽名,第一個(gè)v表示void,意思是callback是沒有返回值的。i表示參數(shù)類型是int。因?yàn)槲覀儷@得的指針類型都是int。還有其他的選項(xiàng)比如f表示float,d表示double等等。string的指針會(huì)自動(dòng)轉(zhuǎn)換為C#的string類型,但是ArrayBuffer需要使用IntPtr指針轉(zhuǎn)換到C#中使用。
????????以上就是主要的在jslib中開啟Web Worker多線程并回調(diào)C#的方式。但是還有一些瑕疵。????
????????1.每次調(diào)用都會(huì)開啟一個(gè)woker線程,但是CPU核心線程是有限的,太多的線程反而會(huì)導(dǎo)致線程之間上下文切換造成額外的性能消耗。推薦使用navigator.hardwareConcurrency獲取客戶端機(jī)器的線程數(shù)來創(chuàng)建一個(gè)Worker池。我的做法是用一個(gè)worker生成另外的幾個(gè)worker來創(chuàng)建并維護(hù)一個(gè)woker池子,并把這個(gè)worker注冊到j(luò)slib中。
????????
????????
????????Google Chrome 17 與 Firefox 18 包含另一種性能更高的方法來將特定類型的對象 (可轉(zhuǎn)讓對象?transferable objects) 傳遞給一個(gè) worker/從 worker 傳回。可轉(zhuǎn)讓對象從一個(gè)上下文轉(zhuǎn)移到另一個(gè)上下文而不會(huì)經(jīng)過任何拷貝操作,類似于指針,因此擁有很高的性能。這里我在workerPool worker和業(yè)務(wù)worker的postMessage中都使用了可轉(zhuǎn)讓對象。對于加載文件類型的數(shù)據(jù),性能提升很大。
????????以上,如果有問題或者建議歡迎在評論區(qū)指正。愛你喲~?