Bietet eine effiziente Allokation kostenlos Async/wartet integriert für die Einheit.
UniTask<T>
und benutzerdefinierte asyncMethodbuilder, um keine Zuordnung zu erzielenUniTask.Yield
, UniTask.Delay
, UniTask.DelayFrame
usw.), die das Ersetzen aller Coroutine -Operationen ermöglichen Für technische Details finden Sie im Blog -Beitrag: Unitask V2 - Zero -Allocation Async/Awesait auf Unity, mit asynchronem Linq
Für fortgeschrittene Tipps siehe Blog -Beitrag: Erweitert UnityWebRequest über das asynchrische Dekorateurmuster - Fortgeschrittene Techniken von Unitask
Installieren Sie über UPM -Paket mit GIT -Referenz- oder Asset -Paket ( UniTask.*.*.*.unitypackage
) in Unitask/Releases erhältlich.
// 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-Funktionen basieren auf C# 7.0 (aufgabenähnliche benutzerdefinierte async-Methoden-Builder-Funktion), sodass die erforderliche Einheit-Version nach Unity 2018.3
ist. Die offizielle, niedrigste Version ist Unity 2018.4.13f1
.
Warum ist Unitask (benutzerdefinierte aufgabenähnliche Objekt) erforderlich? Weil die Aufgabe zu schwer ist und nicht mit Unity-Threading (Single-Thread) übereinstimmt. Unitask verwendet keine Threads und SynchronisationContext/ExecutionContext, da das asynchrone Objekt von Unity automatisch von der Motorschicht von Unity versandt wird. Es erreicht eine schnellere und niedrigere Zuordnung und ist vollständig in die Einheit integriert.
Sie können AsyncOperation
, ResourceRequest
, AssetBundleRequest
, AssetBundleCreateRequest
, UnityWebRequestAsyncOperation
, AsyncGPUReadbackRequest
, IEnumerator
und andere, bei using Cysharp.Threading.Tasks;
.
Unitask bietet drei Muster von Erweiterungsmethoden.
* await asyncOperation ;
* . WithCancellation ( CancellationToken ) ;
* . ToUniTask ( IProgress , PlayerLoopTiming , CancellationToken ) ;
WithCancellation
ist eine einfache Version von ToUniTask
, beide Rückkehr UniTask
. Einzelheiten zur Stornierung finden Sie unter: Abschnitt zur Stornierung und Ausnahme.
HINWEIS: Wait direkt wird vom nativen Timing von PlayerLoop zurückgegeben, aber with Cancellation und Tounitask werden von spezifiziertem PlayerLooptiming zurückgegeben. Einzelheiten zum Timing finden Sie unter: PlayerLoop -Abschnitt.
HINWEIS: AssetBundLequest verfügt über
asset
undallAssets
, die Standardeinstellung wartetasset
. Wenn SieallAssets
erhalten möchten, können SieAwaitForAllAssets()
-Methode verwenden.
Der Typ der UniTask
kann Versorgungsunternehmen wie UniTask.WhenAll
, UniTask.WhenAny
, UniTask.WhenEach
verwenden. Sie sind wie Task.WhenAll
/ Task.WhenAny
, aber der Rückgabetyp ist nützlicher. Sie geben Wert -Tupel zurück, sodass Sie jedes Ergebnis dekonstruieren und mehrere Typen übergeben können.
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 ) ;
}
Wenn Sie einen Rückruf in Unitask konvertieren möchten, können Sie UniTaskCompletionSource<T>
verwenden, eine leichte Ausgabe von 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>
}
Sie können Aufgaben -> Unitask: AsUniTask
, UniTask
-> UniTask<AsyncUnit>
: AsAsyncUnitUniTask
, UniTask<T>
-> UniTask
: AsUniTask
. Die Konvertierungskosten von UniTask<T>
-> UniTask
sind kostenlos.
Wenn Sie Async in Coroutine konvertieren möchten, können Sie .ToCoroutine()
verwenden, dies ist nützlich, wenn Sie nur das Coroutine -System verwenden möchten.
Unitask kann nicht zweimal warten. Dies ist eine ähnliche Einschränkung wie das in .NET Standard 2.1 eingeführte Wert von Valuetask/IvalUetasSource.
Die folgenden Operationen sollten niemals an einer Valuetask -Instanz durchgeführt werden:
- Warten auf die Instanz mehrmals.
- Astask mehrmals anrufen.
- Verwenden Sie .Result oder .GetaWaiter (). GetResult (), wenn der Vorgang noch nicht abgeschlossen ist oder mehrmals verwendet wird.
- Verwenden Sie mehr als eine dieser Techniken, um die Instanz zu konsumieren.
Wenn Sie eines der oben genannten tun, sind die Ergebnisse undefiniert.
var task = UniTask . DelayFrame ( 10 ) ;
await task ;
await task ; // NG, throws Exception
Speichern Sie im Feld Klassen. Sie können UniTask.Lazy
verwenden, das das Aufrufen mehrmals unterstützt. .Preserve()
ermöglicht mehrere Anrufe (intern zwischengespeicherte Ergebnisse). Dies ist nützlich, wenn mehrere Aufrufe in einem Funktionsbereich enthalten sind.
Auch UniTaskCompletionSource
kann mehrmals warten und auf viele Anrufer warten.
Einige Unitask -Fabrikmethoden haben eine CancellationToken cancellationToken = default
haben. Auch einige asynchronisierte Operationen für Einheit haben WithCancellation(CancellationToken)
und ToUniTask(..., CancellationToken cancellation = default)
Erweiterungsmethoden.
Sie können CancellationToken
Parameters durch Standard CancellationTokenSource
übergeben.
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 ) ;
CancellationToken kann durch CancellationTokenSource
oder die Erweiterungsmethode von Monobehaviours GetCancellationTokenOnDestroy
erstellt werden.
// this CancellationToken lifecycle is same as GameObject.
await UniTask . DelayFrame ( 1000 , cancellationToken : this . GetCancellationTokenOnDestroy ( ) ) ;
Für die Propagateströschung empfehlen alle asynchronisierten Methoden, um CancellationToken cancellationToken
zu akzeptieren, die beim letzten Argument storniert und CancellationToken
von Wurzel bis zum Ende übergeben.
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
bedeutet Lebenszyklus von Async. Sie können stattdessen Ihren eigenen Lebenszyklus für die Standardstornierung des Zulassungspunkts halten.
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 ( ) ;
}
}
Nach der Einheit 2022.2 fügt Unity in Monobehaviour eine Stornierung hinzu.
Wenn die Stornierung erkannt wird, werfen alle Methoden OperationCanceledException
aus und propagieren Sie stromaufwärts. Wenn die Ausnahme (nicht auf OperationCanceledException
beschränkt ist) nicht in der asynchronisierten Methode behandelt wird, wird sie schließlich an UniTaskScheduler.UnobservedTaskException
ausgestellt. Das Standardverhalten der empfangenen ungehandelten Ausnahme besteht darin, Protokoll als Ausnahme zu schreiben. Die Protokollebene kann unter Verwendung von UniTaskScheduler.UnobservedExceptionWriteLogType
geändert werden. Wenn Sie ein benutzerdefiniertes Verhalten verwenden möchten, setzen Sie eine Aktion auf UniTaskScheduler.UnobservedTaskException.
Und auch OperationCanceledException
ist eine besondere Ausnahme. Dies wird bei UnobservedTaskException
stillschweigend ignoriert.
Wenn Sie das Verhalten in einer async Unitask -Methode abbrechen möchten, werfen Sie OperationCanceledException
manuell.
public async UniTask < int > FooAsync ( )
{
await UniTask . Yield ( ) ;
throw new OperationCanceledException ( ) ;
}
Wenn Sie eine Ausnahme abwickeln, aber ignorieren möchten (sich an die globale Stornierungsbehandlung ausbreiten), verwenden Sie einen Ausnahmebilter.
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 ;
}
}
Throws/Catch OperationCanceledException
UniTask.SuppressCancellationThrow
etwas schwer. Es kehrt (bool IsCanceled, T Result)
zurück, anstatt zu werfen.
var ( isCanceled , _ ) = await UniTask . DelayFrame ( 10 , cancellationToken : cts . Token ) . SuppressCancellationThrow ( ) ;
if ( isCanceled )
{
// ...
}
HINWEIS: Unterdrücken Sie nur, wenn Sie direkt in die Quellmethode aufrufen. Andernfalls wird der Rückgabewert konvertiert, die gesamte Pipeline unterdrückt jedoch keine Würfe.
Einige Funktionen, die die Spielerschleife von Unity verwenden, wie UniTask.Yield
UniTask.Delay
Dies bedeutet, dass es nicht unmittelbar nach CancellationToken
abgesagt wird.
Wenn Sie dieses Verhalten ändern möchten, stellen Sie die Absage, die sofort abzusagen, das cancelImmediately
als Argument fest.
await UniTask . Yield ( cancellationToken , cancelImmediately : true ) ;
HINWEIS: Das Einstellen cancelImmediately
auf True gesteckt werden und eine sofortige Stornierung erkennen, ist teurer als das Standardverhalten. Dies liegt daran, dass es CancellationToken.Register
verwendet. Register; Es ist schwerer als die Überprüfung von Stornierungen auf der Spielerschleife.
Timeout ist eine Variation der Stornierung. Sie können Timeout durch CancellationTokenSouce.CancelAfterSlim(TimeSpan)
festlegen und an Async -Methoden weitergeben.
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
ist eine Standard -API. In der Einheit sollten Sie es jedoch nicht verwenden, da es sich um den Timing -Timer handelt.CancelAfterSlim
sind die Erweiterungsmethoden von Unitask, sondern verwendet stattdessen PlayerLoop.
Wenn Sie Timeout mit einer anderen Stornierungsquelle verwenden möchten, verwenden Sie 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. " ) ;
}
}
Optimieren Sie für die Reduzierung der Zuordnung von Stornierungen für Timeout pro Anruf asynchronisierter Methode können Sie TimeoutController
von Unitask verwenden.
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 " ) ;
}
}
}
Wenn Sie Timeout mit einer anderen Stornierungsquelle verwenden möchten, verwenden Sie new TimeoutController(CancellationToken)
.
TimeoutController timeoutController ;
CancellationTokenSource clickCancelSource ;
void Start ( )
{
this . clickCancelSource = new CancellationTokenSource ( ) ;
this . timeoutController = new TimeoutController ( clickCancelSource ) ;
}
HINWEIS: Unitask hat .Timeout
, .TimeoutWithoutException
-Methoden jedoch, wenn möglich, diese nicht verwenden, bitte übergeben Sie CancellationToken
. Da .Timeout
von externen Aufgaben funktioniert, kann die Timeoutted -Aufgabe nicht einstellen. .Timeout
bedeutet, dass das Ergebnis beim Timeout ignoriert wird. Wenn Sie eine CancellationToken
an die Methode übergeben, wirkt sie aus der Aufgabe aus, sodass es möglich ist, eine Laufaufgabe zu stoppen.
Einige asynchronisierte Operationen für Einheit haben ToUniTask(IProgress<float> progress = null, ...)
Erweiterungsmethoden.
var progress = Progress . Create < float > ( x => Debug . Log ( x ) ) ;
var request = await UnityWebRequest . Get ( " http://google.co.jp " )
. SendWebRequest ( )
. ToUniTask ( progress : progress ) ;
Sie sollten kein new System.Progress<T>
verwenden. Verwenden Sie stattdessen Cysharp.Threading.Tasks.Progress
. Diese Fortschrittsfabrik hat zwei Methoden, Create
und CreateOnlyValueChanged
. CreateOnlyValueChanged
Anrufe nur dann, wenn sich der Fortschrittswert geändert hat.
Die Implementierung der IProgress -Schnittstelle zu Caller ist besser, da es keine Lambda -Allokation gibt.
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 wird auf einem benutzerdefinierten Playerloop ausgeführt. Die PlayerLoop -basierten Methoden von Unitask (z. B. Delay
, DelayFrame
, asyncOperation.ToUniTask
usw.) akzeptieren diesen PlayerLoopTiming
.
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
}
Wenn Sie ausgeführt werden, können Sie Playerlooplist.md auf die Standard -PlayerLoop von Unity und die benutzerdefinierte Schleife von Unitask überprüfen.
PlayerLoopTiming.Update
ähnelt yield return null
in einer Coroutine, aber es wird vor dem Update (Update- und Ugui -Ereignisse (Button.onclick usw.) aufgerufen, wenn ScriptRunBehaviourUpdate
, Rendite Null, auf ScriptRunDelayedDynamicFrameRate
gerufen wird. PlayerLoopTiming.FixedUpdate
ähnelt dem WaitForFixedUpdate
.
PlayerLoopTiming.LastPostLateUpdate
entspricht nicht deryield return new WaitForEndOfFrame()
von Coroutine. Coroutines Waitforendofframe scheint zu laufen, nachdem der PlayerLoop fertig ist. Einige Methoden, die Coroutines Ende des Rahmens erfordern (Texture2D.ReadPixels
,ScreenCapture.CaptureScreenshotAsTexture
,CommandBuffer
usw.) funktionieren nicht korrekt, wenn sie durch Async/Atert ersetzt werden. In diesen Fällen passieren Monobehaviour (Coroutine Runnner) anUniTask.WaitForEndOfFrame
. Beispielsweiseawait UniTask.WaitForEndOfFrame(this);
ist eine leichte Allokation freie Alternative zuryield return new WaitForEndOfFrame()
.Hinweis:
await UniTask.WaitForEndOfFrame();
Nicht mehr Monobehaviour benötigt. Es verwendetUnityEngine.Awaitable.EndOfFrameAsync
.
yield return null
und UniTask.Yield
sind ähnlich, aber unterschiedlich. yield return null
kehrt immer den nächsten Rahmen zurück, aber UniTask.Yield
kehrt als nächstes an. Das heißt, Call UniTask.Yield(PlayerLoopTiming.Update)
auf PreUpdate
gibt es denselben Rahmen zurück. UniTask.NextFrame()
garantiert die Rückgabe im nächsten Frame. Sie können erwarten, dass sich dies genauso verhalten wie yield return null
.
Unitask.yield (ohne Stornierung) ist ein spezieller Typ, Rendits
YieldAwaitable
und läuft auf RenditeRunner. Es ist das leichte und schnellste.
AsyncOperation
wird vom nativen Timing zurückgegeben. Achten Sie beispielsweise SceneManager.LoadSceneAsync
von EarlyUpdate.UpdatePreloading
zurück und nach dem Aufrufen der geladenen Szene wird der Start
der geladenen Szene von EarlyUpdate.ScriptRunDelayedStartupFrame
aufgerufen. await UnityWebRequest
von EarlyUpdate.ExecuteMainThreadJobs
zurückgegeben wird.
In Unitask verwendet das direkte natives Timing, während WithCancellation
und ToUniTask
das angegebene Timing verwendet. Dies ist normalerweise kein bestimmtes Problem, aber bei LoadSceneAsync
verursacht es eine andere Reihenfolge des Starts und der Fortsetzung nach Warten. Es wird daher empfohlen, LoadSceneAsync.ToUniTask
nicht zu verwenden.
HINWEIS: Stellen Sie bei Verwendung von Unity 2023.1 oder neuer sicher, dass Sie
using UnityEngine;
In der Verwendung von Aussagen Ihrer Datei bei der Arbeit mit NewUnityEngine.Awaitable
-Methoden wieSceneManager.LoadSceneAsync
. Dies verhindert Kompilierungsfehler, indem die Verwendung der Version vonUnityEngine.AsyncOperation
vermieden wird.
Im StackTrace können Sie überprüfen, wo es in PlayerLoop läuft.
Standardmäßig wird die PlayerLoop von Unitask bei [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
initialisiert.
Die Reihenfolge, in der Methoden in BeforeSceneload aufgerufen werden, ist nichtdeterministisch. Wenn Sie also Unitask in anderen Beameload -Methoden verwenden möchten, sollten Sie versuchen, sie vorher zu initialisieren.
// AfterAssembliesLoaded is called before BeforeSceneLoad
[ RuntimeInitializeOnLoadMethod ( RuntimeInitializeLoadType . AfterAssembliesLoaded ) ]
public static void InitUniTaskLoop ( )
{
var loop = PlayerLoop . GetCurrentPlayerLoop ( ) ;
Cysharp . Threading . Tasks . PlayerLoopHelper . Initialize ( ref loop ) ;
}
Wenn Sie Entities
-Paket von Unity importieren, setzt die benutzerdefinierte Player -Schleife bei BeforeSceneLoad
zurück und injiziert die ECS -Schleife. Wenn Unity nach der Initialisierungsmethode von Unitask von ECS die Injektionsmethode aufruft, funktioniert Unitask nicht mehr.
Um dieses Problem zu lösen, können Sie die Unitask PlayerLoop nach der Initialisierung der ECS neu initialisieren.
// Get ECS Loop.
var playerLoop = ScriptBehaviourUpdateOrder . CurrentPlayerLoop ;
// Setup UniTask's PlayerLoop.
PlayerLoopHelper . Initialize ( ref playerLoop ) ;
Sie können diagnostizieren, ob die Player -Schleife von UnitAns bereit ist, indem Sie PlayerLoopHelper.IsInjectedUniTaskPlayerLoop()
anrufen. Und auch PlayerLoopHelper.DumpCurrentPlayerLoop
protokolliert alle aktuellen PlayerLoops für die Konsole.
void Start ( )
{
UnityEngine . Debug . Log ( " UniTaskPlayerLoop ready? " + PlayerLoopHelper . IsInjectedUniTaskPlayerLoop ( ) ) ;
PlayerLoopHelper . DumpCurrentPlayerLoop ( ) ;
}
Sie können die Schleifenkosten geringfügig optimieren, indem Sie die Ungewendungs -PlayerLooptiming -Injektion entfernen. Sie können PlayerLoopHelper.Initialize(InjectPlayerLoopTimings)
auf Initialize anrufen.
var loop = PlayerLoop . GetCurrentPlayerLoop ( ) ;
PlayerLoopHelper . Initialize ( ref loop , InjectPlayerLoopTimings . Minimum ) ; // minimum is Update | FixedUpdate | LastPostLateUpdate
InjectPlayerLoopTimings
verfügt über drei voreingestellte, All
und Standard
(alle ohne letztes außer lastPostlateUpdate), Minimum
( Update | FixedUpdate | LastPostLateUpdate
). Standard ist alles und Sie können benutzerdefinierte Injektionszeiten wie InjectPlayerLoopTimings.Update | InjectPlayerLoopTimings.FixedUpdate | InjectPlayerLoopTimings.PreLateUpdate
kombinieren InjectPlayerLoopTimings.Update | InjectPlayerLoopTimings.FixedUpdate | InjectPlayerLoopTimings.PreLateUpdate
.
Sie können einen Fehler machen, um nicht injizierte PlayerLoopTiming
von microsoft.Codeanalysis.Bannedapianalysatoren zu verwenden. Sie können beispielsweise BannedSymbols.txt
für InjectPlayerLoopTimings.Minimum
einrichten.
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.
Sie können den Schweregrad RS0030
für Fehler konfigurieren.
async void
ist ein Standard -C -# -Tasksystem, sodass es nicht auf Unitask -Systemen ausgeführt wird. Es ist besser, es nicht zu verwenden. async UniTaskVoid
ist eine leichte Version von async UniTask
da sie nicht erwartbarer Abschluss hat und Fehler sofort an UniTaskScheduler.UnobservedTaskException
berichtet. Wenn Sie nicht erwarten müssen (Feuer und vergessen), ist die Verwendung von UniTaskVoid
besser. Leider müssen Sie die Warnung abweisen, Sie müssen Forget()
anrufen.
public async UniTaskVoid FireAndForgetMethod ( )
{
// do anything...
await UniTask . Yield ( ) ;
}
public void Caller ( )
{
FireAndForgetMethod ( ) . Forget ( ) ;
}
Auch Unitask hat die Forget
, sie ähnelt UniTaskVoid
und hat die gleichen Effekte. UniTaskVoid
ist jedoch effizienter, wenn Sie nicht auf。 await
。
public async UniTask DoAsync ( )
{
// do anything...
await UniTask . Yield ( ) ;
}
public void Caller ( )
{
DoAsync ( ) . Forget ( ) ;
}
Verwenden Sie async void
nicht, um ein asynchronisiertes Lambda zu verwenden. Stattdessen können Sie UniTask.Action
oder UniTask.UnityAction
verwenden, die beide über async UniTaskVoid
Lambda einen Delegierten erstellen.
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
kann auch in der Start
von Monobehaviours verwendet werden.
class Sample : MonoBehaviour
{
async UniTaskVoid Start ( )
{
// async init code.
}
}
Nützlich für die Überprüfung (durchgesickerte) Einheiten. Sie können das Trackerfenster im Window -> UniTask Tracker
.
UnitaskTracker ist für das Debuggen nur als Aktivierung von Stacktraces für das Verfolgen und Erfassen von Stacktraces gedacht, hat jedoch eine starke Leistung. Die empfohlene Verwendung besteht darin, sowohl Tracking- als auch Stacktraces zu ermöglichen, Aufgabenlecks zu finden und beides zu deaktivieren, wenn sie fertig sind.
Standardmäßig unterstützt Unitask TextMeshPro ( BindTo(TMP_Text)
und TMP_InputField
-Ereigniserweiterungen wie Standard -UGUI InputField
), dotween ( Tween
als erwartbar) und Adressabteil ( AsyncOperationHandle
und AsyncOperationHandle<T>
als erwartbar).
In getrennten ASMDEFs wie UniTask.TextMeshPro
, UniTask.DOTween
, UniTask.Addressables
.
Der Support von TextMeshPro und Addessables wird beim Importieren ihrer Pakete automatisch aktiviert. Für den doTween -Unterstützung definieren Sie jedoch nach dem Importieren aus den Dot zwischen den Vermögenswerten und Definieren des Skripts Symbol UNITASK_DOTWEEN_SUPPORT
, um dies zu aktivieren.
// 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 ) ) ;
Das Standardverhalten von Dotween Support ( await
, WithCancellation
, ToUniTask
) erwartet, dass Tween getötet wird. Es funktioniert sowohl auf vollständigen (wahr/falschen) als auch auf Kill (wahr/falsch). Aber wenn Sie Tweens ( SetAutoKill(false)
) wiederverwenden möchten, funktioniert es nicht wie erwartet. Wenn Sie auf ein anderes Timing warten möchten, gibt es in Tween die folgenden Erweiterungsmethoden, AwaitForComplete
, AwaitForPause
, AwaitForPlay
, AwaitForRewind
, AwaitForStepComplete
.
Die Unity 2020.2 unterstützt C# 8.0, sodass Sie await foreach
warten können. Dies ist die neue Update -Notation in der asynchronen Ära.
// Unity 2020.2, C# 8.0
await foreach ( var _ in UniTaskAsyncEnumerable . EveryUpdate ( ) . WithCancellation ( token ) )
{
Debug . Log ( " Update() " + Time . frameCount ) ;
}
In einer C# 7.3 -Umgebung können Sie die ForEachAsync
-Methode verwenden, um auf die gleiche Weise zu arbeiten.
// C# 7.3(Unity 2018.3~)
await UniTaskAsyncEnumerable . EveryUpdate ( ) . ForEachAsync ( _ =>
{
Debug . Log ( " Update() " + Time . frameCount ) ;
} , token ) ;
UniTask.WhenEach
, das der Task.WhenEach
von .NET 9 ähnelt. Wheneach kann eine neue Möglichkeit für mehrere Aufgaben verbrauchen.
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 implementiert asynchrones linq, ähnlich wie bei LINQ in IEnumerable<T>
oder rx in IObservable<T>
. Alle Standard -LINQ -Abfragen -Operatoren können auf asynchrone Streams angewendet werden. Der folgende Code zeigt beispielsweise, wie ein Where-Filter auf einen asynchronen Stream angewendet wird, der einmal alle zwei Klicks ausgeführt wird.
await okButton . OnClickAsAsyncEnumerable ( ) . Where ( ( x , i ) => i % 2 == 0 ) . ForEachAsync ( _ =>
{
} ) ;
Fire and Vergessenstil (zum Beispiel Ereignisbearbeitung) können Sie auch Subscribe
verwenden.
okButton . OnClickAsAsyncEnumerable ( ) . Where ( ( x , i ) => i % 2 == 0 ) . Subscribe ( _ =>
{
} ) ;
Async linq ist aktiviert, wenn using Cysharp.Threading.Tasks.Linq;
und UniTaskAsyncEnumerable
ist in UniTask.Linq
ASMDEF definiert.
Es ist näher an Unirx (reaktive Erweiterungen), aber Unitaskasyncenumerable ist ein asynchroner Strombasis, während RX ein pushnterbasierter asynchroner Strom war. Beachten Sie, dass die Eigenschaften, obwohl ähnlich, unterschiedlich sind und die Details mit ihnen unterschiedlich verhalten.
UniTaskAsyncEnumerable
ist der Einstiegspunkt wie Enumerable
. Zusätzlich zu den Standard -Abfragebetreibern gibt es andere Generatoren für Einheit wie EveryUpdate
, Timer
, TimerFrame
, Interval
, IntervalFrame
und EveryValueChanged
. Außerdem fügte zusätzliche ursprüngliche Abfragebetreiber wie Append
, Prepend
, DistinctUntilChanged
, ToHashSet
, Buffer
, CombineLatest
, Merge
Do
, Never
, ForEachAsync
, Queue
, Publish
, Pairwise
, Return
, SkipUntil
, TakeUntil
, SkipUntilCanceled
, TakeUntilCanceled
, Takelastil Subscribe
Abonnement, TakeUntil, TakeUntil, TakeUntil, TakeLast
.
Die Methode mit Func als Argument hat drei zusätzliche Überladungen, ***Await
, ***AwaitWithCancellation
.
Select ( Func < T , TR > selector )
SelectAwait ( Func < T , UniTask < TR > > selector )
SelectAwaitWithCancellation ( Func < T , CancellationToken , UniTask < TR > > selector )
Wenn Sie die async
Methode im Func verwenden möchten, verwenden Sie die ***Await
oder ***AwaitWithCancellation
.
So erstellen Sie einen asynchronen Iterator: C# 8.0 unterstützt den asynchronen Iterator ( async yield return
), ermöglicht jedoch nur IAsyncEnumerable<T>
und erfordert natürlich C# 8.0. Unitask unterstützt UniTaskAsyncEnumerable.Create
-Methode zum Erstellen eines benutzerdefinierten Async -Iterators.
// 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 ( ) ;
}
} ) ;
}
Alle Ugui -Komponenten implementiert ***AsAsyncEnumerable
um asynchrone Ereignisströme umzuwandeln.
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. " ) ;
}
Alle Monobehaviour-Nachrichtenereignisse können Async-Streams durch AsyncTriggers
konvertieren, die durch using Cysharp.Threading.Tasks.Triggers;
. Asynctrigger kann mit GetAsync***Trigger
erstellt werden und löst sich als Unitaskasyncenumerable aus.
var trigger = this . GetOnCollisionEnterAsyncHandler ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
// every moves.
await this . GetAsyncMoveTrigger ( ) . ForEachAsync ( axisEventData =>
{
} ) ;
AsyncReactiveProperty
, AsyncReadOnlyReactiveProperty
ist die Version von ReactiveProperty von Unitask. BindTo
-Erweiterungsmethode von IUniTaskAsyncEnumerable<T>
zur Bindung asynchroner Stream -Werte an Einheitskomponenten (Text/Selektierbar/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 ) ;
Ein asynchroner Stream vom Pull-Typ erhält die nächsten Werte erst, wenn die asynchrone Verarbeitung in der Sequenz abgeschlossen ist. Dies könnte Daten von Push-Typ-Ereignissen wie Schaltflächen verschütten.
// can not get click event during 3 seconds complete.
await button . OnClickAsAsyncEnumerable ( ) . ForEachAwaitAsync ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
Es ist nützlich (Doppelklicken verhindern), aber manchmal nicht nützlich.
Die Verwendung der Queue()
-Methode wird auch während der asynchronen Verarbeitung Ereignisse anstellen.
// queued message in asynchronous processing
await button . OnClickAsAsyncEnumerable ( ) . Queue ( ) . ForEachAwaitAsync ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
Oder verwenden Sie Subscribe
, Feuer und Vergessen Sie Stil.
button . OnClickAsAsyncEnumerable ( ) . Subscribe ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
Channel
ist der gleiche wie bei System.threading.tasks.Channels, die einem Golang -Kanal ähnlich sind.
Derzeit unterstützt es nur Mehrfachproduzent, Single-Consumer-unbegrenzte Kanäle. Es kann durch Channel.CreateSingleConsumerUnbounded<T>()
erzeugt werden.
Verwenden Sie für Produzent ( .Writer
) TryWrite
um den Wert zu verschieben und den Kanal zu TryComplete
. Verwenden Sie für Consumer ( .Reader
) TryRead
, WaitToReadAsync
, ReadAsync
, Completion
und ReadAllAsync
, um Warteschlangenmeldungen zu lesen.
ReadAllAsync
gibt IUniTaskAsyncEnumerable<T>
also linq -Operatoren zurück. Der Reader erlaubt nur Single-Consumer, verwendet jedoch .Publish()
Query-Operator, um Multicast-Nachricht zu aktivieren. Machen Sie beispielsweise Pub/Sub -Nutzen.
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 ( ) ;
}
}
Einheit 6 führt den wartbaren Typ ein, der wartbar ist. Einfach ausgedrückt kann wartbar als Teilmenge von Unitask angesehen werden, und tatsächlich wurde das Design von Actableable von Unitask beeinflusst. Es sollte in der Lage sein, auf PlayerLoop-basierte Erwartungen, gepoolten Aufgaben und Unterstützung für die Stornierung mit CancellationToken
auf ähnliche Weise zu handhaben. Mit seiner Aufnahme in die Standardbibliothek können Sie sich fragen, ob Sie Unitask fortsetzen oder nach auf wartetbar migrieren sollen. Hier ist eine kurze Anleitung.
Erstens entspricht die von Actable bereitgestellte Funktionen, die den Coroutinen anbieten. Anstelle yield return
erwarten Sie. await NextFrameAsync()
ersetzt yield return null
; Und es gibt Äquivalente für WaitForSeconds
und EndOfFrame
. Das ist jedoch das Ausmaß. In Bezug auf die Funktionalität auf Coroutine basiert es nicht auf aufgabenbasierte Funktionen. In der praktischen Anwendungsentwicklung unter Verwendung von Async/Auseait sind Operationen wie WhenAll
wesentlich. Darüber hinaus ermöglicht Unitask viele rahmenbasierte Operationen (wie DelayFrame
) und eine flexiblere PlayerLooptiming-Steuerung, die nicht erwartbar sind. Natürlich gibt es auch kein Trackerfenster.
Daher empfehle ich die Verwendung von Unitask für die Anwendungsentwicklung. Unitask ist eine übertreffende Superset und enthält viele wesentliche Funktionen. Für die Entwicklung der Bibliothek wäre es angemessen, externe Abhängigkeiten zu vermeiden, wenn Sie als Rückgabetyp für Methoden erwartet werden. Es kann erwartbar sind, kann mit AsUniTask
in Unitask konvertiert werden, sodass in der Unitask-Bibliothek keine Probleme beim Umgang mit wartbaren Funktionen in der Unitasking-Bibliothek vorhanden sind. Wenn Sie sich keine Sorgen um Abhängigkeiten machen müssen, wäre es natürlich die beste Wahl, selbst für die Bibliotheksentwicklung die beste Wahl zu haben.
Das [UnityTest]
-Matrikt von Unity kann Coroutine (IEnumerator) testen, aber Async nicht testen. UniTask.ToCoroutine
Bridges asynchron/wartet auf Coroutine, damit Sie asynchronisierte Methoden testen können.
[ 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 ;
}
} ) ;
Die eigenen Unit -Tests von Unitask werden mit Unity Test Runner und Cysharp/Runtimeunittesttoolkit geschrieben, um sich in CI zu integrieren und zu überprüfen, ob IL2CPP funktioniert.
Die meisten Unitask -Methoden werden auf einem einzelnen Thread (PlayerLoop) ausgeführt, wobei nur UniTask.Run
( Task.Run
Equivalent) und UniTask.SwitchToThreadPool
auf einem Thread -Pool ausgeführt werden. Wenn Sie einen Thread -Pool verwenden, funktioniert er nicht mit WebGL und so weiter.
UniTask.Run
ist jetzt veraltet. Sie können stattdessen UniTask.RunOnThreadPool
verwenden. Überlegen Sie auch, ob Sie UniTask.Create
oder UniTask.Void
verwenden können.
Sie können Coroutine (IEnumerator) in Unitask konvertieren (oder direkt warten), hat jedoch einige Einschränkungen.
WaitForEndOfFrame
/ WaitForFixedUpdate
/ Coroutine
wird nicht unterstützt.StartCoroutine
, es verwendet das angegebene PlayerLoopTiming
und das Standard PlayerLoopTiming.Update
wird vor Update
von Monobehaviours und StartCoroutine
von Monobehaviours ausgeführt. Wenn Sie eine vollständig kompatible Konvertierung von Coroutine in Async wünschen, verwenden Sie die Überlastung von IEnumerator.ToUniTask(MonoBehaviour coroutineRunner)
. Es führt StartCoroutine in einer Instanz des Arguments Monobehaviour aus und wartet darauf, dass es in Unitask abgeschlossen ist.
Unitask kann wie eine Editor Coroutine auf Unity Editor ausgeführt. Es gibt jedoch einige Einschränkungen.
DelayType.Realtime
zu verzögern, die auf den richtigen Zeitpunkt warten.EditorApplication.update
.-batchmode
mit -quit
funktioniert nicht, da Unity nach einem einzelnen Frame keine EditorApplication.update
ausführt und beendet. Verwenden Sie stattdessen nicht -quit
und kündigen Sie manuell mit EditorApplication.Exit(0)
. Unitask verfügt über viele aufgabenähnliche Standard-APIs. Diese Tabelle zeigt, was die alternativen APIs sind.
Verwenden Sie Standardtyp.
.NET -Typ | Unitask -Typ |
---|---|
IProgress<T> | --- |
CancellationToken | --- |
CancellationTokenSource | --- |
Verwenden Sie den Typ Unitask.
.NET -Typ | Unitask -Typ |
---|---|
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 kann aggressiv async versprechen Objekte, um keine Zuordnung zu erzielen (technische Details finden Sie unter Blog -Post -Unitask -V2 - Null -Allocation Async/Assait auf Unity, mit asynchronem Linq). Standardmäßig werden alle Versprechen zwischengespeichert, aber Sie können TaskPool.SetMaxPoolSize
auf Ihren Wert konfigurieren. Der Wert zeigt die Cache -Größe pro Typ an. TaskPool.GetCacheSizeInfo
gibt derzeit zwischengespeicherte Objekte im Pool zurück.
foreach ( var ( type , size ) in TaskPool . GetCacheSizeInfo ( ) )
{
Debug . Log ( type + " : " + size ) ;
}
In UnityEditor zeigt der Profiler die Zuweisung von Compiler, die asyncstatemachine erzeugt werden, er tritt jedoch nur im Debug (Entwicklung) auf. C# Compiler generiert asyncStatemachine als Klasse für Debug -Build und als Struktur für die Release -Build.
Unity unterstützt die Code -Optimierungsoption ab 2020.1 (rechts, Fußzeile).
Sie können die C# Compiler -Optimierung in die Freigabe ändern, um die asyncstatemachine -Allokation in Entwicklungsbauten zu entfernen. Diese Optimierungsoption kann auch über Compilation.CompilationPipeline-codeOptimization
und Compilation.CodeOptimization
festgelegt werden.
Die Standard -Synchronisationcontext von Unity ( UnitySynchronizationContext
) ist eine schlechte Implementierung für die Leistung. Unitask umgeht SynchronizationContext
(und ExecutionContext
), sodass es nicht verwendet wird, aber wenn es in async Task
existiert, wird es immer noch verwendet. UniTaskSynchronizationContext
ist ein Ersatz von UnitySynchronizationContext
, der für die Leistung besser ist.
public class SyncContextInjecter
{
[ RuntimeInitializeOnLoadMethod ( RuntimeInitializeLoadType . SubsystemRegistration ) ]
public static void Inject ( )
{
SynchronizationContext . SetSynchronizationContext ( new UniTaskSynchronizationContext ( ) ) ;
}
}
Dies ist eine optionale Wahl und wird nicht immer empfohlen. UniTaskSynchronizationContext
ist weniger leistungsfähig als async UniTask
und kein vollständiger Unitask -Austausch. Es garantiert auch keine vollständige Verhaltenskompatibilität mit dem UnitySynchronizationContext
.
Die API -Referenzen von Unitask werden unter Cysharp.github.io/unitask von Docfx und Cysharp/docfxtemplate gehostet.
Beispielsweise können die Fabrikmethoden von Unitask bei Unitask#Methoden angezeigt werden. Die Fabrik-/Erweiterungsmethoden von Unitaskasyncenumerable finden Sie unter Unitaskasyncenumerable#-Methoden.
Erfordert eine Version der Einheit, die den Parameter des Pfadabfrage für GIT -Pakete unterstützt (Unity> = 2019.3.4f1, Einheit> = 2020.1A21). Sie können https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
hinzufügen, um Manager zu packen
Oder fügen Sie "com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"
zu Packages/manifest.json
.
Wenn Sie eine Zielversion festlegen möchten, verwendet Unitask das Tag *.*.*
Damit Sie eine Version wie #2.1.0
angeben können. Zum Beispiel https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0
.
Verwenden Sie für .NET Core Nuget.
PM> PACKAGE UNITASK
Die Unitask von .NET Core -Version ist eine Untergruppe von Unity Unitask, wobei PlayerLoop -abhängige Methoden entfernt wurden.
Es wird mit einer höheren Leistung ausgeführt als die Standardaufgabe/das Standard -Task/das Valuetask. Sie sollten jedoch darauf achten, den ExecutionContext/SynchronizationContext bei der Verwendung zu ignorieren. AsyncLocal
funktioniert auch nicht, da es executionContext ignoriert.
Wenn Sie Unitask intern verwenden, aber Valuetask als externe API bereitstellen, können Sie es wie die folgenden (inspiriert von PooledaWait) schreiben.
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 -Version soll es Benutzern ermöglichen, Unitask als Schnittstelle zu verwenden, wenn Code mit Einheit (wie Cysharp/Magiconion) freigegeben wird. .NET CORE -Version von Unitask ermöglicht eine reibungslose Codefreigabe.
Versorgungsmethoden wie beispielsweise, die dem Unitask entsprechen, werden als Cysharp/Valuetasks -Supplement bereitgestellt.
Diese Bibliothek befindet sich unter der MIT -Lizenz.