為統一提供有效的無異步分配/等待集成。
UniTask<T>
和自定義異步分組以實現零分配UniTask.Yield
, UniTask.Delay
, UniTask.DelayFrame
等),可以更換所有Coroutine操作有關技術詳細信息,請參閱博客文章:Unitask V2 - 零分配異步/等待Unity,以及異步LINQ
有關高級提示,請參見博客文章:通過異步裝飾器圖案擴展Unitywebrequest - Unitask的高級技術
通過UPM軟件包安裝git參考或資產軟件包( UniTask.*.*.*.unitypackage
),可在Unitask/發布中提供。
// extension awaiter/methods can be used by this namespace
using Cysharp . Threading . Tasks ;
// You can return type as struct UniTask<T>(or UniTask), it is unity specialized lightweight alternative of Task<T>
// zero allocation and fast excution for zero overhead async/await integrate with Unity
async UniTask < string > DemoAsync ( )
{
// You can await Unity's AsyncObject
var asset = await Resources . LoadAsync < TextAsset > ( " foo " ) ;
var txt = ( await UnityWebRequest . Get ( " https://... " ) . SendWebRequest ( ) ) . downloadHandler . text ;
await SceneManager . LoadSceneAsync ( " scene2 " ) ;
// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
// after Unity 2022.2, you can use `destroyCancellationToken` in MonoBehaviour
var asset2 = await Resources . LoadAsync < TextAsset > ( " bar " ) . WithCancellation ( this . GetCancellationTokenOnDestroy ( ) ) ;
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
var asset3 = await Resources . LoadAsync < TextAsset > ( " baz " ) . ToUniTask ( Progress . Create < float > ( x => Debug . Log ( x ) ) ) ;
// await frame-based operation like a coroutine
await UniTask . DelayFrame ( 100 ) ;
// replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
await UniTask . Delay ( TimeSpan . FromSeconds ( 10 ) , ignoreTimeScale : false ) ;
// yield any playerloop timing(PreUpdate, Update, LateUpdate, etc...)
await UniTask . Yield ( PlayerLoopTiming . PreLateUpdate ) ;
// replacement of yield return null
await UniTask . Yield ( ) ;
await UniTask . NextFrame ( ) ;
// replacement of WaitForEndOfFrame
# if UNITY_2023_1_OR_NEWER
await UniTask . WaitForEndOfFrame ( ) ;
# else
// requires MonoBehaviour(CoroutineRunner))
await UniTask . WaitForEndOfFrame ( this ) ; // this is MonoBehaviour
#endif
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
await UniTask . WaitForFixedUpdate ( ) ;
// replacement of yield return WaitUntil
await UniTask . WaitUntil ( ( ) => isActive == false ) ;
// special helper of WaitUntil
await UniTask . WaitUntilValueChanged ( this , x => x . isActive ) ;
// You can await IEnumerator coroutines
await FooCoroutineEnumerator ( ) ;
// You can await a standard task
await Task . Run ( ( ) => 100 ) ;
// Multithreading, run on ThreadPool under this code
await UniTask . SwitchToThreadPool ( ) ;
/* work on ThreadPool */
// return to MainThread(same as `ObserveOnMainThread` in UniRx)
await UniTask . SwitchToMainThread ( ) ;
// get async webrequest
async UniTask < string > GetTextAsync ( UnityWebRequest req )
{
var op = await req . SendWebRequest ( ) ;
return op . downloadHandler . text ;
}
var task1 = GetTextAsync ( UnityWebRequest . Get ( " http://google.com " ) ) ;
var task2 = GetTextAsync ( UnityWebRequest . Get ( " http://bing.com " ) ) ;
var task3 = GetTextAsync ( UnityWebRequest . Get ( " http://yahoo.com " ) ) ;
// concurrent async-wait and get results easily by tuple syntax
var ( google , bing , yahoo ) = await UniTask . WhenAll ( task1 , task2 , task3 ) ;
// shorthand of WhenAll, tuple can await directly
var ( google2 , bing2 , yahoo2 ) = await ( task1 , task2 , task3 ) ;
// return async-value.(or you can use `UniTask`(no result), `UniTaskVoid`(fire and forget)).
return ( asset as TextAsset ) ? . text ?? throw new InvalidOperationException ( " Asset not found " ) ;
}
unitask功能依賴於C#7.0(類似任務的自定義ASYNC方法構建器功能),因此所需的Unity版本是在Unity 2018.3
之後,支持的官方最低版本是Unity 2018.4.13f1
。
為什麼需要使用Unitask(自定義任務樣本對象)?因為任務太重,與Unity線程(單線程)不匹配。 unitask不使用線程和同步context/executionContext,因為Unity的發動機層自動調度了Unity的異步對象。它實現了更快和降低的分配,並且與統一完全集成在一起。
您可以等待AsyncOperation
, ResourceRequest
, AssetBundleRequest
, AssetBundleCreateRequest
, UnityWebRequestAsyncOperation
, AsyncGPUReadbackRequest
,iEnumerator,ienumerator, IEnumerator
和其他using Cysharp.Threading.Tasks;
。
Unitask提供了三種擴展方法的模式。
* await asyncOperation ;
* . WithCancellation ( CancellationToken ) ;
* . ToUniTask ( IProgress , PlayerLoopTiming , CancellationToken ) ;
WithCancellation
是ToUniTask
的簡單版本,均為返回UniTask
。有關取消的詳細信息,請參見:取消和異常處理部分。
注意:等待直接從playerloop的本機時間返回,但與指定的playerlooptiming一起返回了cancellation和tounitask。有關定時的詳細信息,請參見:PlayerLoop部分。
注意:AssetBundLereQuest具有
asset
和allAssets
,默認值等待返回asset
。如果要獲得allAssets
,則可以使用AwaitForAllAssets()
方法。
UniTask
的類型可以使用UniTask.WhenAll
, UniTask.WhenAny
, UniTask.WhenEach
等實用程序。它們就像Task.WhenAll
/ Task.WhenAny
當時,返回類型更有用。它們返回值元組,因此您可以解構每個結果並傳遞多種類型。
public async UniTaskVoid LoadManyAsync ( )
{
// parallel load.
var ( a , b , c ) = await UniTask . WhenAll (
LoadAsSprite ( " foo " ) ,
LoadAsSprite ( " bar " ) ,
LoadAsSprite ( " baz " ) ) ;
}
async UniTask < Sprite > LoadAsSprite ( string path )
{
var resource = await Resources . LoadAsync < Sprite > ( path ) ;
return ( resource as Sprite ) ;
}
如果要將回調轉換為unitask,則可以使用UniTaskCompletionSource<T>
這是TaskCompletionSource<T>
輕量級版本。
public UniTask < int > WrapByUniTaskCompletionSource ( )
{
var utcs = new UniTaskCompletionSource < int > ( ) ;
// when complete, call utcs.TrySetResult();
// when failed, call utcs.TrySetException();
// when cancel, call utcs.TrySetCanceled();
return utcs . Task ; //return UniTask<int>
}
您可以轉換任務 - > unitask: AsUniTask
, UniTask
> UniTask<AsyncUnit>
: AsAsyncUnitUniTask
, UniTask<T>
- > UniTask
: AsUniTask
。 UniTask<T>
- > UniTask
的轉換成本是免費的。
如果要將異步轉換為coroutine,則可以使用.ToCoroutine()
,如果您只允許使用Coroutine系統,這很有用。
Unitask不能等待兩次。這與.NET標準2.1中引入的Valuetask/ivaluetasksource相似。
不應在Valuetask實例上執行以下操作:
- 多次等待實例。
- 多次致電ASTAKS。
- 使用.result或.getawaiter()。getResult()操作尚未完成或多次使用它們。
- 使用這些技術中的多種消耗實例。
如果您執行上述任何操作,則結果是不確定的。
var task = UniTask . DelayFrame ( 10 ) ;
await task ;
await task ; // NG, throws Exception
存儲到類字段,您可以使用UniTask.Lazy
支持多次調用。 .Preserve()
允許多個呼叫(內部緩存結果)。當功能範圍中有多個調用時,這很有用。
同樣, UniTaskCompletionSource
可以等待多次,並等待許多呼叫者。
某些Unitask工廠方法具有CancellationToken cancellationToken = default
參數。同樣,某些統一的異步操作具有與WithCancellation(CancellationToken)
和ToUniTask(..., CancellationToken cancellation = default)
擴展方法的擴展方法。
您可以通過標準CancellationTokenSource
將CancellationToken
傳遞給參數。
var cts = new CancellationTokenSource ( ) ;
cancelButton . onClick . AddListener ( ( ) =>
{
cts . Cancel ( ) ;
} ) ;
await UnityWebRequest . Get ( " http://google.co.jp " ) . SendWebRequest ( ) . WithCancellation ( cts . Token ) ;
await UniTask . DelayFrame ( 1000 , cancellationToken : cts . Token ) ;
可以通過CancellationTokenSource
或Monobehaviour的擴展方法GetCancellationTokenOnDestroy
創建取消token。
// this CancellationToken lifecycle is same as GameObject.
await UniTask . DelayFrame ( 1000 , cancellationToken : this . GetCancellationTokenOnDestroy ( ) ) ;
對於傳播取消,所有異步方法都建議在最終參數中接受CancellationToken cancellationToken
,然後將CancellationToken
從根到結束。
await FooAsync ( this . GetCancellationTokenOnDestroy ( ) ) ;
// ---
async UniTask FooAsync ( CancellationToken cancellationToken )
{
await BarAsync ( cancellationToken ) ;
}
async UniTask BarAsync ( CancellationToken cancellationToken )
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) , cancellationToken ) ;
}
CancellationToken
表示異步的生命週期。您可以持有自己的生命週期,而代替默認的comcellationTokenEndestroy。
public class MyBehaviour : MonoBehaviour
{
CancellationTokenSource disableCancellation = new CancellationTokenSource ( ) ;
CancellationTokenSource destroyCancellation = new CancellationTokenSource ( ) ;
private void OnEnable ( )
{
if ( disableCancellation != null )
{
disableCancellation . Dispose ( ) ;
}
disableCancellation = new CancellationTokenSource ( ) ;
}
private void OnDisable ( )
{
disableCancellation . Cancel ( ) ;
}
private void OnDestroy ( )
{
destroyCancellation . Cancel ( ) ;
destroyCancellation . Dispose ( ) ;
}
}
在Unity 2022.2之後,Unity在monobehaviour.destroycancellationtoken和application.exitCancellationToken中添加了concellationToken。
當檢測到取消時,所有方法都會拋出OperationCanceledException
並在上游傳播。如果不使用異步方法處理異常(不限於OperationCanceledException
),則最終將其傳播到UniTaskScheduler.UnobservedTaskException
。未接收的例外的默認行為是將日誌寫為例外。可以使用UniTaskScheduler.UnobservedExceptionWriteLogType
更改日誌級別。如果要使用自定義行為,請將操作設置為UniTaskScheduler.UnobservedTaskException.
而且OperationCanceledException
是一個特殊的例外,在UnobservedTaskException
時,這被默默地忽略。
如果您想在異步一鍵方法中取消行為,請手動扔OperationCanceledException
。
public async UniTask < int > FooAsync ( )
{
await UniTask . Yield ( ) ;
throw new OperationCanceledException ( ) ;
}
如果您處理異常,但想忽略(傳播到全局取消處理),請使用異常過濾器。
public async UniTask < int > BarAsync ( )
{
try
{
var x = await FooAsync ( ) ;
return x * 2 ;
}
catch ( Exception ex ) when ( ! ( ex is OperationCanceledException ) ) // when (ex is not OperationCanceledException) at C# 9.0
{
return - 1 ;
}
}
投擲/捕獲OperationCanceledException
稍微沉重,因此,如果性能是一個問題,請使用UniTask.SuppressCancellationThrow
,以避免使用操作cancanceledexception throw。它返回(bool IsCanceled, T Result)
而不是投擲。
var ( isCanceled , _ ) = await UniTask . DelayFrame ( 10 , cancellationToken : cts . Token ) . SuppressCancellationThrow ( ) ;
if ( isCanceled )
{
// ...
}
注意:僅當您直接呼叫到最源方法時,只會抑制投擲。否則,返回值將被轉換,但是整個管道不會抑制投擲。
某些使用Unity播放器循環的功能,例如UniTask.Yield
和UniTask.Delay
等,確定了播放器循環上的comcellationToken狀態。這意味著它不會在CancellationToken
射擊後立即取消。
如果您想更改此行為,則立即取消將其設置為參數,將cancelImmediately
為comment。
await UniTask . Yield ( cancellationToken , cancelImmediately : true ) ;
注意:將cancelImmediately
設置為True並檢測立即取消的設置比默認行為更為昂貴。這是因為它使用CancellationToken.Register
;它比在播放器循環上檢查取消token的重量更重。
超時是取消的變體。您可以通過CancellationTokenSouce.CancelAfterSlim(TimeSpan)
設置超時,然後將comcellationToken傳遞到異步方法。
var cts = new CancellationTokenSource ( ) ;
cts . CancelAfterSlim ( TimeSpan . FromSeconds ( 5 ) ) ; // 5sec timeout.
try
{
await UnityWebRequest . Get ( " http://foo " ) . SendWebRequest ( ) . WithCancellation ( cts . Token ) ;
}
catch ( OperationCanceledException ex )
{
if ( ex . CancellationToken == cts . Token )
{
UnityEngine . Debug . Log ( " Timeout " ) ;
}
}
CancellationTokenSouce.CancelAfter
是標準API。但是,在統一上,您不應該使用它,因為它取決於線程計時器。CancelAfterSlim
是Unitask的擴展方法,它使用PlayerLoop。
如果要將超時與其他取消源一起使用,請使用CancellationTokenSource.CreateLinkedTokenSource
。
var cancelToken = new CancellationTokenSource ( ) ;
cancelButton . onClick . AddListener ( ( ) =>
{
cancelToken . Cancel ( ) ; // cancel from button click.
} ) ;
var timeoutToken = new CancellationTokenSource ( ) ;
timeoutToken . CancelAfterSlim ( TimeSpan . FromSeconds ( 5 ) ) ; // 5sec timeout.
try
{
// combine token
var linkedTokenSource = CancellationTokenSource . CreateLinkedTokenSource ( cancelToken . Token , timeoutToken . Token ) ;
await UnityWebRequest . Get ( " http://foo " ) . SendWebRequest ( ) . WithCancellation ( linkedTokenSource . Token ) ;
}
catch ( OperationCanceledException ex )
{
if ( timeoutToken . IsCancellationRequested )
{
UnityEngine . Debug . Log ( " Timeout. " ) ;
}
else if ( cancelToken . IsCancellationRequested )
{
UnityEngine . Debug . Log ( " Cancel clicked. " ) ;
}
}
優化以減少concellationTokenSource的分配,以進行每個呼叫async方法的超時,您可以使用Unitask的TimeoutController
。
TimeoutController timeoutController = new TimeoutController ( ) ; // setup to field for reuse.
async UniTask FooAsync ( )
{
try
{
// you can pass timeoutController.Timeout(TimeSpan) to cancellationToken.
await UnityWebRequest . Get ( " http://foo " ) . SendWebRequest ( )
. WithCancellation ( timeoutController . Timeout ( TimeSpan . FromSeconds ( 5 ) ) ) ;
timeoutController . Reset ( ) ; // call Reset(Stop timeout timer and ready for reuse) when succeed.
}
catch ( OperationCanceledException ex )
{
if ( timeoutController . IsTimeout ( ) )
{
UnityEngine . Debug . Log ( " timeout " ) ;
}
}
}
如果要將超時與其他取消源一起使用,請使用new TimeoutController(CancellationToken)
。
TimeoutController timeoutController ;
CancellationTokenSource clickCancelSource ;
void Start ( )
{
this . clickCancelSource = new CancellationTokenSource ( ) ;
this . timeoutController = new TimeoutController ( clickCancelSource ) ;
}
注意:unitask具有.Timeout
, .TimeoutWithoutException
方法,但是,如果可能的話,請勿使用這些方法,請通過CancellationToken
。因為.Timeout
.Timeout
表示超時時忽略結果。如果將CancellationToken
傳遞給該方法,它將從任務內部起作用,因此可以停止運行任務。
統一的某些異步操作具有ToUniTask(IProgress<float> progress = null, ...)
擴展方法。
var progress = Progress . Create < float > ( x => Debug . Log ( x ) ) ;
var request = await UnityWebRequest . Get ( " http://google.co.jp " )
. SendWebRequest ( )
. ToUniTask ( progress : progress ) ;
您不應使用標準的new System.Progress<T>
。使用Cysharp.Threading.Tasks.Progress
。該進度工廠有兩種方法,分別Create
和CreateOnlyValueChanged
。僅當進度值更改時, CreateOnlyValueChanged
呼叫。
將IProgress接口實現到呼叫者更好,因為沒有LAMBDA分配。
public class Foo : MonoBehaviour , IProgress < float >
{
public void Report ( float value )
{
UnityEngine . Debug . Log ( value ) ;
}
public async UniTaskVoid WebRequest ( )
{
var request = await UnityWebRequest . Get ( " http://google.co.jp " )
. SendWebRequest ( )
. ToUniTask ( progress : this ) ; // pass this
}
}
unitask在自定義playerloop上運行。基於unitask的PlayerLoopTiming
方法(例如Delay
, DelayFrame
, asyncOperation.ToUniTask
。
public enum PlayerLoopTiming
{
Initialization = 0 ,
LastInitialization = 1 ,
EarlyUpdate = 2 ,
LastEarlyUpdate = 3 ,
FixedUpdate = 4 ,
LastFixedUpdate = 5 ,
PreUpdate = 6 ,
LastPreUpdate = 7 ,
Update = 8 ,
LastUpdate = 9 ,
PreLateUpdate = 10 ,
LastPreLateUpdate = 11 ,
PostLateUpdate = 12 ,
LastPostLateUpdate = 13
# if UNITY_2020_2_OR_NEWER
TimeUpdate = 14 ,
LastTimeUpdate = 15 ,
#endif
}
它指示何時運行,您可以檢查playerlooplist.md到Unity的默認PlayerLoop和注入Unitask的自定義循環。
PlayerLoopTiming.Update
類似於在coroutine中的yield return null
,但是在更新之前稱為(更新和UGUI事件(button.onclick等...),在ScriptRunBehaviourUpdate
上調用, ScriptRunDelayedDynamicFrameRate
在scriptrunbehaviourupdate上稱為null)。 PlayerLoopTiming.FixedUpdate
類似於WaitForFixedUpdate
。
PlayerLoopTiming.LastPostLateUpdate
不等於Coroutine的yield return new WaitForEndOfFrame()
。 Coroutine的Waitforendofframe似乎在完成PlayerLoop完成後運行。一些需要Coroutine末端的方法(Texture2D.ReadPixels
,ScreenCapture.CaptureScreenshotAsTexture
,CommandBuffer
等)在替換異步/等待時無法正常工作。在這些情況下,將Monobehaviour(Coroutine Runnner)傳遞到UniTask.WaitForEndOfFrame
。例如,await UniTask.WaitForEndOfFrame(this);
是輕巧的分配免費替代yield return new WaitForEndOfFrame()
。注意:在Unity 2023.1或更新中,
await UniTask.WaitForEndOfFrame();
不再需要Monobehaviour。它使用UnityEngine.Awaitable.EndOfFrameAsync
。
yield return null
和UniTask.Yield
相似但不同。 yield return null
總是返回下一個幀,但UniTask.Yield
返回接下來。也就是說,請致電UniTask.Yield(PlayerLoopTiming.Update)
在PreUpdate
上,它返回同一幀。 UniTask.NextFrame()
保證返回下一個幀,您可以期望這與yield return null
完全相同。
unitask.yield(無取消用語)是一種特殊的類型,返回
YieldAwaitable
,並在faredrunner上運行。它是最輕巧,最快的。
AsyncOperation
從本機時間返回。例如,等待SceneManager.LoadSceneAsync
從EarlyUpdate.UpdatePreloading
返回,在被稱為後,已加載的場景的Start
是從EarlyUpdate.ScriptRunDelayedStartupFrame
調用的。同樣, await UnityWebRequest
從EarlyUpdate.ExecuteMainThreadJobs
返回。
在Unitask中,等待直接使用本機定時,而WithCancellation
和ToUniTask
使用指定的時機。這通常不是一個特定的問題,但是對於LoadSceneAsync
,它會在等待後導致不同的起始和延續順序。因此,建議不要使用LoadSceneAsync.ToUniTask
。
注意:使用Unity 2023.1或更新時,請確保
using UnityEngine;
在使用新的UnityEngine.Awaitable
時,在文件的使用語句中,如SceneManager.LoadSceneAsync
。通過避免使用UnityEngine.AsyncOperation
版本,這可以防止編譯錯誤。
在StackTrace中,您可以在PlayerLoop中檢查其在哪裡運行。
默認情況下,Unitask的PlayerLoop在[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
中進行了初始化。
在beforesceneload中調用方法的順序是非確定性的,因此,如果要在其他beforesceneload方法中使用unitask,則應在此之前嘗試初始化它。
// AfterAssembliesLoaded is called before BeforeSceneLoad
[ RuntimeInitializeOnLoadMethod ( RuntimeInitializeLoadType . AfterAssembliesLoaded ) ]
public static void InitUniTaskLoop ( )
{
var loop = PlayerLoop . GetCurrentPlayerLoop ( ) ;
Cysharp . Threading . Tasks . PlayerLoopHelper . Initialize ( ref loop ) ;
}
如果您導入Unity的Entities
軟件包,則將自定義播放器循環重置為在BeforeSceneLoad
上默認並註入ECS的循環。當Unity在Unitask的初始化方法之後調用Unity調用ECS的入口方法時,Unitask將不再起作用。
為了解決此問題,您可以在ECS初始化後重新啟動Unitask PlayerLoop。
// Get ECS Loop.
var playerLoop = ScriptBehaviourUpdateOrder . CurrentPlayerLoop ;
// Setup UniTask's PlayerLoop.
PlayerLoopHelper . Initialize ( ref playerLoop ) ;
您可以通過調用PlayerLoopHelper.IsInjectedUniTaskPlayerLoop()
來診斷Unitask的播放器循環是否準備就緒。以及PlayerLoopHelper.DumpCurrentPlayerLoop
log log s sonemole當前的playerLoops。
void Start ( )
{
UnityEngine . Debug . Log ( " UniTaskPlayerLoop ready? " + PlayerLoopHelper . IsInjectedUniTaskPlayerLoop ( ) ) ;
PlayerLoopHelper . DumpCurrentPlayerLoop ( ) ;
}
您可以通過刪除未使用播放器浮動注入來稍微優化循環成本。您可以在初始化時調用PlayerLoopHelper.Initialize(InjectPlayerLoopTimings)
。
var loop = PlayerLoop . GetCurrentPlayerLoop ( ) ;
PlayerLoopHelper . Initialize ( ref loop , InjectPlayerLoopTimings . Minimum ) ; // minimum is Update | FixedUpdate | LastPostLateUpdate
InjectPlayerLoopTimings
具有三個預設, All
和Standard
(除了LastPostLateUpdate外沒有最後一個預設), Minimum
( Update | FixedUpdate | LastPostLateUpdate
)。默認值是全部,您可以組合自定義注射時間,例如InjectPlayerLoopTimings.Update | InjectPlayerLoopTimings.FixedUpdate | InjectPlayerLoopTimings.PreLateUpdate
。
您可以通過Microsoft.codeanalysis.bannedapianalyzers使用未註入的PlayerLoopTiming
來犯錯。例如,您可以將類似的BannedSymbols.txt
設置為InjectPlayerLoopTimings.Minimum
。
F:Cysharp.Threading.Tasks.PlayerLoopTiming.Initialization; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastInitialization; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.EarlyUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastEarlyUpdate; Isn't injected this PlayerLoop in this project.d
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastFixedUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.PreUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastPreUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.PreLateUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastPreLateUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.PostLateUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.TimeUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastTimeUpdate; Isn't injected this PlayerLoop in this project.
您可以將RS0030
嚴重性配置為錯誤。
async void
是標準C#任務系統,因此它不在unitask系統上運行。最好不要使用它。 async UniTaskVoid
是async UniTask
的輕量級版本,因為它沒有等待的完成,並立即向UniTaskScheduler.UnobservedTaskException
報告錯誤。unobsevedtaskexception。如果您不需要等待(火和忘記),則使用UniTaskVoid
會更好。不幸的是要駁回警告,您需要打電話Forget()
。
public async UniTaskVoid FireAndForgetMethod ( )
{
// do anything...
await UniTask . Yield ( ) ;
}
public void Caller ( )
{
FireAndForgetMethod ( ) . Forget ( ) ;
}
此外,unitask具有Forget
方法,它類似於UniTaskVoid
,並且具有相同的效果。但是UniTaskVoid
如果您完全不使用await
。
public async UniTask DoAsync ( )
{
// do anything...
await UniTask . Yield ( ) ;
}
public void Caller ( )
{
DoAsync ( ) . Forget ( ) ;
}
要使用註冊到事件的異步lambda,請不要使用async void
。取而代之的是,您可以使用UniTask.Action
或UniTask.UnityAction
,這兩者都通過async UniTaskVoid
lambda創建一個代表。
Action actEvent ;
UnityAction unityEvent ; // especially used in uGUI
// Bad: async void
actEvent += async ( ) => { } ;
unityEvent += async ( ) => { } ;
// Ok: create Action delegate by lambda
actEvent += UniTask . Action ( async ( ) => { await UniTask . Yield ( ) ; } ) ;
unityEvent += UniTask . UnityAction ( async ( ) => { await UniTask . Yield ( ) ; } ) ;
UniTaskVoid
也可以在Monobehaviour的Start
方法中使用。
class Sample : MonoBehaviour
{
async UniTaskVoid Start ( )
{
// async init code.
}
}
可用於檢查(洩漏)的unitask。您可以在Window -> UniTask Tracker
。
UnitaskTracker旨在僅用於調試使用,因為啟用跟踪和捕獲stackTraces非常有用,但性能影響很大。推薦的用法是啟用跟踪和stackTraces以查找任務洩漏並在完成後將其禁用。
默認情況下,Unitask支持TextMeshPro( BindTo(TMP_Text)
和TMP_InputField
事件Extensions,例如標準UGUI InputField
),dotwee Tween
(等待)和Promersables( AsyncOperationHandle
和asyncoperationHandle和AsyncOperationHandle<T>
等待等待)。
在分離的ASMDEF中定義了UniTask.TextMeshPro
, UniTask.DOTween
, UniTask.Addressables
。
從軟件包管理器導入其軟件包時,TextMeshPro和PromereAbles支持將自動啟用。但是,對於支持之間的支持,從資產之間導入並定義腳本定義符號UNITASK_DOTWEEN_SUPPORT
以啟用它。
// sequential
await transform . DOMoveX ( 2 , 10 ) ;
await transform . DOMoveZ ( 5 , 20 ) ;
// parallel with cancellation
var ct = this . GetCancellationTokenOnDestroy ( ) ;
await UniTask . WhenAll (
transform . DOMoveX ( 10 , 3 ) . WithCancellation ( ct ) ,
transform . DOScale ( 10 , 3 ) . WithCancellation ( ct ) ) ;
支持支持的默認行為( await
, WithCancellation
, ToUniTask
)等待Tween被殺死。它在完整(true/fals)和殺死(true/false)上都起作用。但是,如果您想重複使用Tweens( SetAutoKill(false)
),則它無法正常工作。如果您想等待另一個時間安排,則在Tween, AwaitForComplete
, AwaitForPause
Pape, AwaitForPlay
, AwaitForRewind
AwaitForStepComplete
中存在以下擴展方法。
Unity 2020.2支持C#8.0,因此您可以使用await foreach
。這是異步時代的新更新表示法。
// Unity 2020.2, C# 8.0
await foreach ( var _ in UniTaskAsyncEnumerable . EveryUpdate ( ) . WithCancellation ( token ) )
{
Debug . Log ( " Update() " + Time . frameCount ) ;
}
在C#7.3環境中,您可以使用ForEachAsync
方法以幾乎相同的方式工作。
// C# 7.3(Unity 2018.3~)
await UniTaskAsyncEnumerable . EveryUpdate ( ) . ForEachAsync ( _ =>
{
Debug . Log ( " Update() " + Time . frameCount ) ;
} , token ) ;
UniTask.WhenEach
與.NET 9的Task.WhenEach
相似。Wheneach可以消除等待多個任務的新方法。
await foreach ( var result in UniTask . WhenEach ( task1 , task2 , task3 ) )
{
// The result is of type WhenEachResult<T>.
// It contains either `T Result` or `Exception Exception`.
// You can check `IsCompletedSuccessfully` or `IsFaulted` to determine whether to access `.Result` or `.Exception`.
// If you want to throw an exception when `IsFaulted` and retrieve the result when successful, use `GetResult()`.
Debug . Log ( result . GetResult ( ) ) ;
}
unitaskAsyncenumerable會激發異步LINQ,類似於IObservable<T>
IEnumerable<T>
<t>中的linq或rx。所有標準的LINQ查詢操作員都可以應用於異步流。例如,以下代碼顯示瞭如何將Where濾波器應用於單擊每兩個點擊一次的按鈕單與同步流。
await okButton . OnClickAsAsyncEnumerable ( ) . Where ( ( x , i ) => i % 2 == 0 ) . ForEachAsync ( _ =>
{
} ) ;
火與忘記樣式(例如,事件處理),您也可以使用Subscribe
。
okButton . OnClickAsAsyncEnumerable ( ) . Where ( ( x , i ) => i % 2 == 0 ) . Subscribe ( _ =>
{
} ) ;
using Cysharp.Threading.Tasks.Linq;
, UniTaskAsyncEnumerable
在UniTask.Linq
asmdef中定義。
它更接近unirx(反應性擴展),但是unitaskAsyncenumerable是一種基於拉動的異步流,而RX是基於推動的異步流。請注意,儘管相似,但特徵是不同的,細節與它們一起行為不同。
UniTaskAsyncEnumerable
是Enumerable
的入口點。除了標準查詢運算符外,還有其他用於統一的發電機,例如EveryUpdate
, Timer
, TimerFrame
, Interval
, IntervalFrame
和EveryValueChanged
。 And also added additional UniTask original query operators like Append
, Prepend
, DistinctUntilChanged
, ToHashSet
, Buffer
, CombineLatest
, Merge
Do
, Never
, ForEachAsync
, Pairwise
, Publish
, Queue
, Return
, SkipUntil
, TakeUntil
, SkipUntilCanceled
, TakeUntilCanceled
, TakeLast
, Subscribe
.
以彈性為論點的方法還有三個額外的過載, ***Await
, ***AwaitWithCancellation
。
Select ( Func < T , TR > selector )
SelectAwait ( Func < T , UniTask < TR > > selector )
SelectAwaitWithCancellation ( Func < T , CancellationToken , UniTask < TR > > selector )
如果您想在func內使用async
方法,請使用***Await
或***AwaitWithCancellation
。
如何創建異步迭代器:C#8.0支持異步迭代器( async yield return
),但它僅允許IAsyncEnumerable<T>
,當然也需要C#8.0。 unitask支持UniTaskAsyncEnumerable.Create
方法來創建自定義異步迭代器。
// IAsyncEnumerable, C# 8.0 version of async iterator. ( do not use this style, IAsyncEnumerable is not controled in UniTask).
public async IAsyncEnumerable < int > MyEveryUpdate ( [ EnumeratorCancellation ] CancellationToken cancelationToken = default )
{
var frameCount = 0 ;
await UniTask . Yield ( ) ;
while ( ! token . IsCancellationRequested )
{
yield return frameCount ++ ;
await UniTask . Yield ( ) ;
}
}
// UniTaskAsyncEnumerable.Create and use `await writer.YieldAsync` instead of `yield return`.
public IUniTaskAsyncEnumerable < int > MyEveryUpdate ( )
{
// writer(IAsyncWriter<T>) has `YieldAsync(value)` method.
return UniTaskAsyncEnumerable . Create < int > ( async ( writer , token ) =>
{
var frameCount = 0 ;
await UniTask . Yield ( ) ;
while ( ! token . IsCancellationRequested )
{
await writer . YieldAsync ( frameCount ++ ) ; // instead of `yield return`
await UniTask . Yield ( ) ;
}
} ) ;
}
所有UGUI組件都實現了***AsAsyncEnumerable
可轉換異步事件流。
async UniTask TripleClick ( )
{
// In default, used button.GetCancellationTokenOnDestroy to manage lieftime of async
await button . OnClickAsync ( ) ;
await button . OnClickAsync ( ) ;
await button . OnClickAsync ( ) ;
Debug . Log ( " Three times clicked " ) ;
}
// more efficient way
async UniTask TripleClick ( )
{
using ( var handler = button . GetAsyncClickEventHandler ( ) )
{
await handler . OnClickAsync ( ) ;
await handler . OnClickAsync ( ) ;
await handler . OnClickAsync ( ) ;
Debug . Log ( " Three times clicked " ) ;
}
}
// use async LINQ
async UniTask TripleClick ( CancellationToken token )
{
await button . OnClickAsAsyncEnumerable ( ) . Take ( 3 ) . Last ( ) ;
Debug . Log ( " Three times clicked " ) ;
}
// use async LINQ2
async UniTask TripleClick ( CancellationToken token )
{
await button . OnClickAsAsyncEnumerable ( ) . Take ( 3 ) . ForEachAsync ( _ =>
{
Debug . Log ( " Every clicked " ) ;
} ) ;
Debug . Log ( " Three times clicked, complete. " ) ;
}
所有monobehaviour消息事件都可以通過使用cysharp.threading.tasks.triggers啟用異步來轉換異步流的AsyncTriggers
流using Cysharp.Threading.Tasks.Triggers;
。可以使用GetAsync***Trigger
創建異步,並觸發自己作為unitaskAsyncenumerable。
var trigger = this . GetOnCollisionEnterAsyncHandler ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
// every moves.
await this . GetAsyncMoveTrigger ( ) . ForEachAsync ( axisEventData =>
{
} ) ;
AsyncReactiveProperty
, AsyncReadOnlyReactiveProperty
是Unitask的反應性Property版本。 IUniTaskAsyncEnumerable<T>
的BindTo
擴展方法<t>用於綁定異步流值與統一組件(文本/可選/TMP/TEXT)。
var rp = new AsyncReactiveProperty < int > ( 99 ) ;
// AsyncReactiveProperty itself is IUniTaskAsyncEnumerable, you can query by LINQ
rp . ForEachAsync ( x =>
{
Debug . Log ( x ) ;
} , this . GetCancellationTokenOnDestroy ( ) ) . Forget ( ) ;
rp . Value = 10 ; // push 10 to all subscriber
rp . Value = 11 ; // push 11 to all subscriber
// WithoutCurrent ignore initial value
// BindTo bind stream value to unity components.
rp . WithoutCurrent ( ) . BindTo ( this . textComponent ) ;
await rp . WaitAsync ( ) ; // wait until next value set
// also exists ToReadOnlyAsyncReactiveProperty
var rp2 = new AsyncReactiveProperty < int > ( 99 ) ;
var rorp = rp . CombineLatest ( rp2 , ( x , y ) => ( x , y ) ) . ToReadOnlyAsyncReactiveProperty ( CancellationToken . None ) ;
直到序列中的異步處理完成之前,吸水型異步流才能獲得下一個值。這可能會從按鈕等按鈕類型事件(例如按鈕)溢出數據。
// can not get click event during 3 seconds complete.
await button . OnClickAsAsyncEnumerable ( ) . ForEachAwaitAsync ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
它很有用(防止雙擊),但有時沒有用。
使用Queue()
方法還將在異步處理過程中排隊事件。
// queued message in asynchronous processing
await button . OnClickAsAsyncEnumerable ( ) . Queue ( ) . ForEachAwaitAsync ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
或使用Subscribe
,開火和忘記樣式。
button . OnClickAsAsyncEnumerable ( ) . Subscribe ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
Channel
與system.threading.tasks.channels相同,該通道類似於Golang通道。
目前,它僅支持多生產者單官量無界的通道。它可以通過Channel.CreateSingleConsumerUnbounded<T>()
創建。
對於生產者( .Writer
),請使用TryWrite
推動值,然後TryComplete
完成頻道。對於消費者( .Reader
),請使用TryRead
, WaitToReadAsync
, ReadAsync
, Completion
和ReadAllAsync
讀取排隊的消息。
ReadAllAsync
返回IUniTaskAsyncEnumerable<T>
SO查詢Linq運算符。閱讀器僅允許單算物,但使用.Publish()
查詢操作員啟用多播消息。例如,製作酒吧/子實用程序。
public class AsyncMessageBroker < T > : IDisposable
{
Channel < T > channel ;
IConnectableUniTaskAsyncEnumerable < T > multicastSource ;
IDisposable connection ;
public AsyncMessageBroker ( )
{
channel = Channel . CreateSingleConsumerUnbounded < T > ( ) ;
multicastSource = channel . Reader . ReadAllAsync ( ) . Publish ( ) ;
connection = multicastSource . Connect ( ) ; // Publish returns IConnectableUniTaskAsyncEnumerable.
}
public void Publish ( T value )
{
channel . Writer . TryWrite ( value ) ;
}
public IUniTaskAsyncEnumerable < T > Subscribe ( )
{
return multicastSource ;
}
public void Dispose ( )
{
channel . Writer . TryComplete ( ) ;
connection . Dispose ( ) ;
}
}
Unity 6介紹了期待的類型,等待。簡而言之,可以將等待的人視為一部分unitask,實際上,等待的設計受unitask的影響。它應該能夠以類似的方式處理基於PlayerLoop的等待,匯總任務並支持取消CancellationToken
。由於它包含在標準庫中,您可能會想知道是繼續使用unitask還是遷移到等待。這是一個簡短的指南。
首先,等待的功能等同於Coroutines提供的功能。您沒有yield return
,而是要等待; await NextFrameAsync()
替代yield return null
; WaitForSeconds
和EndOfFrame
的等效物。但是,這就是它的程度。就功能而言,基於Coroutine,它缺乏基於任務的功能。在使用異步/等待的實際應用開發中,諸如WhenAll
必不可少的操作。此外,Unitask啟用許多基於框架的操作(例如DelayFrame
)和更靈活的PlayerLooptiming Control,這在等待中不可用。當然,也沒有跟踪器窗口。
因此,我建議將Unitask用於應用程序開發。 Unitask是等待的超集,並包含許多基本功能。對於圖書館開發,您想避免外部依賴關係,將等待作為方法的返回類型是適當的。可以使用AsUniTask
轉換為unitask,因此處理Unitask庫中的基於等待的功能沒有問題。當然,如果您不必擔心依賴關係,那麼即使對於圖書館開發來說,使用Unitask也是最佳選擇。
Unity的[UnityTest]
屬性可以測試Coroutine(Ienumerator),但無法測試異步。 UniTask.ToCoroutine
橋異步/等待coroutine,因此您可以測試異步方法。
[ UnityTest ]
public IEnumerator DelayIgnore ( ) => UniTask . ToCoroutine ( async ( ) =>
{
var time = Time . realtimeSinceStartup ;
Time . timeScale = 0.5f ;
try
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) , ignoreTimeScale : true ) ;
var elapsed = Time . realtimeSinceStartup - time ;
Assert . AreEqual ( 3 , ( int ) Math . Round ( TimeSpan . FromSeconds ( elapsed ) . TotalSeconds , MidpointRounding . ToEven ) ) ;
}
finally
{
Time . timeScale = 1.0f ;
}
} ) ;
Unitask自己的單元測試是使用Unity Test Runner和CYSHARP/RUNTIMENITTESTTOOLKIT編寫的,以與CI集成,並檢查IL2CPP是否有效。
大多數unitask方法都在單個線程(PlayerLoop)上運行,僅具有UniTask.Run
( Task.Run
oporcorent)和UniTask.SwitchToThreadPool
在線程池上運行。如果您使用線程池,則它將與WebGL一起使用,依此類推。
UniTask.Run
現在已棄用。您可以使用UniTask.RunOnThreadPool
。還要考慮您是否可以使用UniTask.Create
或UniTask.Void
。
您可以將Coroutine(Ienumerator)轉換為unitask(或直接等待),但它有一些局限性。
WaitForEndOfFrame
/ WaitForFixedUpdate
/ Coroutine
。StartCoroutine
不同,它使用指定的PlayerLoopTiming
和默認的PlayerLoopTiming.Update
在Monobehaviour的Update
和StartCoroutine
的循環之前運行。如果您想完全兼容從coroutine到異步,請使用IEnumerator.ToUniTask(MonoBehaviour coroutineRunner)
過載。它在參數monobehaviour的實例上執行StartCoroutine,並等待在Unitask中完成。
Unitask可以像編輯Coroutine一樣在Unity編輯器上運行。但是,有一些局限性。
DelayType.Realtime
等待合適的時間。EditorApplication.update
上運行。-batchmode
with -quit
不起作用,因為Unity不運行EditorApplication.update
並在單個幀之後退出。相反,請勿使用EditorApplication.Exit(0)
手動使用-quit
和quit。 Unitask具有許多標準的任務狀API。該表顯示了替代API是什麼。
使用標準類型。
.NET類型 | unitask類型 |
---|---|
IProgress<T> | --- |
CancellationToken | --- |
CancellationTokenSource | --- |
使用Unitask類型。
.NET類型 | unitask類型 |
---|---|
Task / ValueTask | UniTask |
Task<T> / ValueTask<T> | UniTask<T> |
async void | async UniTaskVoid |
+= async () => { } | UniTask.Void , UniTask.Action , UniTask.UnityAction |
--- | UniTaskCompletionSource |
TaskCompletionSource<T> | UniTaskCompletionSource<T> / AutoResetUniTaskCompletionSource<T> |
ManualResetValueTaskSourceCore<T> | UniTaskCompletionSourceCore<T> |
IValueTaskSource | IUniTaskSource |
IValueTaskSource<T> | IUniTaskSource<T> |
ValueTask.IsCompleted | UniTask.Status.IsCompleted() |
ValueTask<T>.IsCompleted | UniTask<T>.Status.IsCompleted() |
new Progress<T> | Progress.Create<T> |
CancellationToken.Register(UnsafeRegister) | CancellationToken.RegisterWithoutCaptureExecutionContext |
CancellationTokenSource.CancelAfter | CancellationTokenSource.CancelAfterSlim |
Channel.CreateUnbounded<T>(false){ SingleReader = true } | Channel.CreateSingleConsumerUnbounded<T> |
IAsyncEnumerable<T> | IUniTaskAsyncEnumerable<T> |
IAsyncEnumerator<T> | IUniTaskAsyncEnumerator<T> |
IAsyncDisposable | IUniTaskAsyncDisposable |
Task.Delay | UniTask.Delay |
Task.Yield | UniTask.Yield |
Task.Run | UniTask.RunOnThreadPool |
Task.WhenAll | UniTask.WhenAll |
Task.WhenAny | UniTask.WhenAny |
Task.WhenEach | UniTask.WhenEach |
Task.CompletedTask | UniTask.CompletedTask |
Task.FromException | UniTask.FromException |
Task.FromResult | UniTask.FromResult |
Task.FromCanceled | UniTask.FromCanceled |
Task.ContinueWith | UniTask.ContinueWith |
TaskScheduler.UnobservedTaskException | UniTaskScheduler.UnobservedTaskException |
unitask積極地緩存異步承諾對象實現零分配(有關技術詳細信息,請參見博客文章unitask v2 - 零分配異步/等待unity,以及異步的linq)。默認情況下,它緩存了所有承諾,但是您可以將TaskPool.SetMaxPoolSize
配置為您的值,該值表示每種類型的高速緩存大小。 TaskPool.GetCacheSizeInfo
返回當前緩存的對象。
foreach ( var ( type , size ) in TaskPool . GetCacheSizeInfo ( ) )
{
Debug . Log ( type + " : " + size ) ;
}
在Unityeditor中,Profiler顯示了編譯器生成異步的分配,但僅發生在調試(開發)構建中。 C#編譯器生成了Asyncstatemachine作為DEBUG構建中的類,並在發行版本上作為結構。
Unity從2020.1開始支持代碼優化選項(右,頁腳)。
您可以更改C#編譯器優化以釋放以刪除開發構建中的Asyncstatemachine分配。也可以通過Compilation.CompilationPipeline-codeOptimization
設置Compilation.CodeOptimization
優化選項。
Unity的默認同步性電信( UnitySynchronizationContext
)是性能的差。 unitask繞過SynchronizationContext
(和ExecutionContext
),因此它不使用它,但是如果在async Task
中存在,則仍然使用它。 UniTaskSynchronizationContext
是UnitySynchronizationContext
的替代,這是更好的性能。
public class SyncContextInjecter
{
[ RuntimeInitializeOnLoadMethod ( RuntimeInitializeLoadType . SubsystemRegistration ) ]
public static void Inject ( )
{
SynchronizationContext . SetSynchronizationContext ( new UniTaskSynchronizationContext ( ) ) ;
}
}
這是一個可選的選擇,並不總是建議; UniTaskSynchronizationContext
性能低於async UniTask
,並且不是完整的unitask替換。它還不能保證與UnitySynchronizationContext
的完全行為兼容性。
Unitask的API參考文獻由DOCFX和CYSHARP/DOCFXTEMPLATE託管在cysharp.github.io/unitask上。
例如,可以在Unitask#方法上看到Unitask的工廠方法。可以在unitaskAsyncenumerable#方法上看到unitaskAsyncenumerable的工廠/擴展方法。
需要一個統一版本,該版本支持GIT軟件包的路徑查詢參數(Unity> = 2019.3.4f1,Unity> = 2020.1A21)。您可以添加https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
到包裝管理器
或添加"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"
Packages/manifest.json
如果要設置目標版本,unitask使用*.*.*
釋放標籤,以便指定#2.1.0
之類的版本。例如https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0
。
對於.NET核心,請使用Nuget。
pm>安裝包裝一式裝置
.NET CORE版本的Unitask是Unity Unitass的子集,並刪除了PlayerLoop依賴性方法。
它以高於標準任務/ValueTask的性能運行,但是在使用時,您應該小心忽略ExecutionContext/SynchronizationContext。 AsyncLocal
也不起作用,因為它忽略了executionContext。
如果您在內部使用Unitask,但將ValueTask作為外部API提供,則可以像以下內容一樣編寫(受Pomedawait的啟發)。
public class ZeroAllocAsyncAwaitInDotNetCore
{
public ValueTask < int > DoAsync ( int x , int y )
{
return Core ( this , x , y ) ;
static async UniTask < int > Core ( ZeroAllocAsyncAwaitInDotNetCore self , int x , int y )
{
// do anything...
await Task . Delay ( TimeSpan . FromSeconds ( x + y ) ) ;
await UniTask . Yield ( ) ;
return 10 ;
}
}
}
// UniTask does not return to original SynchronizationContext but you can use helper `ReturnToCurrentSynchronizationContext`.
public ValueTask TestAsync ( )
{
await using ( UniTask . ReturnToCurrentSynchronizationContext ( ) )
{
await UniTask . SwitchToThreadPool ( ) ;
// do anything..
}
}
.NET Core版本旨在允許用戶在與Unity共享代碼時使用Unitask作為接口(例如Cysharp/Magiconion)。 。
提供等同於unitask的何時提供的實用方法作為cysharp/valueTaskSuppplement。
該圖書館屬於麻省理工學院許可證。