Proporciona una asignia de asignación eficiente de asignación/integro de espera para la unidad.
UniTask<T>
y asyncmethodbuilder personalizado para lograr la asignación ceroUniTask.Yield
, UniTask.Delay
, UniTask.DelayFrame
, etc.) que habilita el reemplazo de todas las operaciones de Coroutine Para obtener detalles técnicos, consulte la publicación del blog: AITASK V2 - Asíncipe de asignación cero/espera para la unidad, con Linq asíncrono
Para obtener consejos avanzados, consulte la publicación del blog: Extiende UnityWebRequest a través del patrón de decorador de asíncrono - Técnicas avanzadas de Unitask
Instale a través del paquete UPM con referencia GIT o paquete de activos ( UniTask.*.*.*.unitypackage
) Disponible en UNITASK/LARGADOS.
// 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 " ) ;
}
Las funciones de Unitask dependen de C# 7.0 (función de constructor de métodos Async personalizados con forma de tarea), por lo que la versión de Unity requerida es después de Unity 2018.3
, la versión oficial más baja admitida es Unity 2018.4.13f1
.
¿Por qué se requiere unitarask (objeto de tarea personalizado)? Porque la tarea es demasiado pesada y no coincide con un enhebrado de Unity (un solo hilo). Unitask no usa hilos y sincronizaciónContext/ExecutionContext porque el objeto asíncrono de Unity es automático enviado por la capa del motor de Unity. Logra una asignación más rápida y más baja, y está completamente integrado con la unidad.
Puede esperar AsyncOperation
, ResourceRequest
, AssetBundleRequest
, AssetBundleCreateRequest
, UnityWebRequestAsyncOperation
, AsyncGPUReadbackRequest
, IEnumerator
y otros cuando using Cysharp.Threading.Tasks;
.
AiteSk proporciona tres patrones de métodos de extensión.
* await asyncOperation ;
* . WithCancellation ( CancellationToken ) ;
* . ToUniTask ( IProgress , PlayerLoopTiming , CancellationToken ) ;
WithCancellation
es una versión simple de ToUniTask
, ambos de regreso UniTask
. Para obtener detalles de la cancelación, consulte: Sección de cancelación y manejo de excepciones.
NOTA: La esperanza directamente se devuelve del momento nativo de Playerloop, pero con la cancelación y Tounitask se devuelven de PlayerLooptiming especificado. Para obtener detalles sobre el tiempo, consulte: Sección Playerloop.
NOTA: AssetBundLerequest tiene
asset
yallAssets
, por defecto esperaasset
de devoluciones. Si desea obtenerallAssets
, puede usar el métodoAwaitForAllAssets()
.
El tipo de UniTask
puede usar utilidades como UniTask.WhenAll
, UniTask.WhenAny
, UniTask.WhenEach
. Son como Task.WhenAll
/ Task.WhenAny
, pero el tipo de retorno es más útil. Devuelven las tuplas de valor para que pueda deconstruir cada resultado y pasar múltiples tipos.
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 ) ;
}
Si desea convertir una devolución de llamada a Unitask, puede usar UniTaskCompletionSource<T>
, que es una edición liviana de 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>
}
Puede convertir la tarea -> Unitask: AsUniTask
, UniTask
-> UniTask<AsyncUnit>
: AsAsyncUnitUniTask
, UniTask<T>
-> UniTask
: AsUniTask
. UniTask<T>
-> El costo de conversión de UniTask
es gratuito.
Si desea convertir async a Coroutine, puede usar .ToCoroutine()
, esto es útil si solo desea permitir el uso del sistema Coroutine.
Unitek no puede esperar dos veces. Esta es una restricción similar al ValuEtask/iValuetAskSource introducido en .NET Standard 2.1.
Las siguientes operaciones nunca deben realizarse en una instancia de valueTask:
- Esperando la instancia varias veces.
- Llamar a Astask varias veces.
- Usando .Result o .getAwaIter (). GetResult () cuando la operación aún no se ha completado, o usándolos varias veces.
- Uso de más de una de estas técnicas para consumir la instancia.
Si hace alguno de los anteriores, los resultados no están definidos.
var task = UniTask . DelayFrame ( 10 ) ;
await task ;
await task ; // NG, throws Exception
Almacene en el campo de clase, puede usar UniTask.Lazy
que admite llamar varias veces. .Preserve()
permite múltiples llamadas (resultados en caché internamente). Esto es útil cuando hay múltiples llamadas en un alcance de funciones.
También UniTaskCompletionSource
puede esperar varias veces y esperar de muchas personas que llaman.
Algunos métodos de fábrica de Unitask tienen un parámetro CancellationToken cancellationToken = default
. Además, algunas operaciones de Async para Unity tienen métodos de extensión WithCancellation(CancellationToken)
y ToUniTask(..., CancellationToken cancellation = default)
.
Puede pasar CancellationToken
al parámetro por CancellationTokenSource
estándar.
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 se puede crear mediante CancellationTokenSource
o el método de extensión de Monobehaviour GetCancellationTokenOnDestroy
.
// this CancellationToken lifecycle is same as GameObject.
await UniTask . DelayFrame ( 1000 , cancellationToken : this . GetCancellationTokenOnDestroy ( ) ) ;
Para la cancelación de propagación, todo el método de asíncrono recomienda aceptar CancellationToken cancellationToken
en el último argumento, y pasar CancellationToken
de raíz hasta final.
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
significa ciclo de vida de Async. Puede contener su propio ciclo de vida en su lugar de cancelación predeterminada de TokenDestroy.
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 ( ) ;
}
}
Después de la Unity 2022.2, Unity agrega cancelación en monobehaviour.destoycancellationToken y aplicación.exitCancellationToken.
Cuando se detecta la cancelación, todos los métodos lanzan OperationCanceledException
y se propagan aguas arriba. Cuando la excepción (no se limita a OperationCanceledException
) no se maneja en el método Async, finalmente se propaga a UniTaskScheduler.UnobservedTaskException
. El comportamiento predeterminado de la excepción no controlada recibida es escribir el registro como excepción. El nivel de registro se puede cambiar usando UniTaskScheduler.UnobservedExceptionWriteLogType
. Si desea utilizar el comportamiento personalizado, establezca una acción en UniTaskScheduler.UnobservedTaskException.
Y también OperationCanceledException
es una excepción especial, esto se ignora silenciosamente en UnobservedTaskException
.
Si desea cancelar el comportamiento en un método Async AiteSk, lanza OperationCanceledException
manualmente.
public async UniTask < int > FooAsync ( )
{
await UniTask . Yield ( ) ;
throw new OperationCanceledException ( ) ;
}
Si maneja una excepción pero desea ignorar (propague al manejo de cancelación global), use un filtro de excepción.
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
de lanzamiento/captura es ligeramente pesado, por lo que si el rendimiento es una preocupación, use UniTask.SuppressCancellationThrow
. Regresa (bool IsCanceled, T Result)
en lugar de lanzar.
var ( isCanceled , _ ) = await UniTask . DelayFrame ( 10 , cancellationToken : cts . Token ) . SuppressCancellationThrow ( ) ;
if ( isCanceled )
{
// ...
}
Nota: Solo suprima los lanzamientos si llama directamente al método más fuente. De lo contrario, el valor de retorno se convertirá, pero toda la tubería no suprimirá los lanzamientos.
Algunas características que usan el bucle de jugadores de Unity, como UniTask.Yield
y UniTask.Delay
etc., determina la cancelación de estado de Token en el bucle del jugador. Esto significa que no se cancela inmediatamente después de que CancellationToken
se disparó.
Si desea cambiar este comportamiento, la cancelación para ser inmediata, establezca el indicador cancelImmediately
como un argumento.
await UniTask . Yield ( cancellationToken , cancelImmediately : true ) ;
Nota: Establecer cancelImmediately
en verdadero y detectar una cancelación inmediata es más costoso que el comportamiento predeterminado. Esto se debe a que usa CancellationToken.Register
; Es más pesado que verificar cancelationToken en el bucle del jugador.
El tiempo de espera es una variación de cancelación. Puede establecer TimeOut por CancellationTokenSouce.CancelAfterSlim(TimeSpan)
y pasar CancellationToken a los métodos Async.
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
es una API estándar. Sin embargo, en la unidad no debe usarlo porque depende del temporizador de subprocesos.CancelAfterSlim
es los métodos de extensión de UITEK, utiliza Playerloop.
Si desea utilizar el tiempo de espera con otra fuente de cancelación, use 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. " ) ;
}
}
Optimizar para reducir la asignación de cancelationTokenSource para el tiempo de espera por tiempo del método Async, puede usar TimeoutController
de Unitask.
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 " ) ;
}
}
}
Si desea utilizar el tiempo de espera con otra fuente de cancelación, use new TimeoutController(CancellationToken)
.
TimeoutController timeoutController ;
CancellationTokenSource clickCancelSource ;
void Start ( )
{
this . clickCancelSource = new CancellationTokenSource ( ) ;
this . timeoutController = new TimeoutController ( clickCancelSource ) ;
}
NOTA: UNITASK tiene .Timeout
, .TimeoutWithoutException
Métodos Sin embargo, si es posible, no use estos, pase CancellationToken
. Porque. .Timeout
trabajo desde externo de la tarea, no puede detener la tarea de tiempo de espera. .Timeout
significa ignorar el resultado cuando el tiempo de espera. Si pasa una CancellationToken
al método, actuará desde el interior de la tarea, por lo que es posible detener una tarea de ejecución.
Algunas operaciones de async para la unidad tienen métodos de extensión 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 ) ;
No debe usar new System.Progress<T>
, porque causa asignación cada vez. Use Cysharp.Threading.Tasks.Progress
en su lugar. Esta fábrica de progreso tiene dos métodos, Create
y crear y CreateOnlyValueChanged
. CreateOnlyValueChanged
Llamadas solo cuando el valor de progreso ha cambiado.
Implementar la interfaz IProgress a la persona que llama es mejor, ya que no hay asignación 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
}
}
Unitek se ejecuta en un jugador personalizado. Métodos basados en PlayerLoop de UNITASK (como Delay
, DelayFrame
, asyncOperation.ToUniTask
, etc ...) Acepte esta 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
}
Indica cuándo ejecutarse, puede verificar PlayerLoopList.md al Playerloop predeterminado de Unity e inyectar el bucle personalizado de UNITEASK.
PlayerLoopTiming.Update
es similar a yield return null
en una corutina, pero se llama antes de la actualización (actualización y eventos UGUI (botón.enclick, etc ...) se llaman en ScriptRunBehaviourUpdate
, el rendimiento nulo se llama a ScriptRunDelayedDynamicFrameRate
). PlayerLoopTiming.FixedUpdate
es similar a WaitForFixedUpdate
.
PlayerLoopTiming.LastPostLateUpdate
no es equivalente alyield return new WaitForEndOfFrame()
. Waitforendofframe de Coroutine parece funcionar después de que el jugador de Playerloop está terminado. Algunos métodos que requieren el final del marco de Coroutine (Texture2D.ReadPixels
,ScreenCapture.CaptureScreenshotAsTexture
,CommandBuffer
, etc.) no funcionan correctamente cuando se reemplazan con async/espera. En estos casos, pase Monobehaviour (Coroutine Runnner) aUniTask.WaitForEndOfFrame
. WAITFORENDOFFRAME. Por ejemplo,await UniTask.WaitForEndOfFrame(this);
es una alternativa gratuita de asignación de peso ligeroyield return new WaitForEndOfFrame()
.Nota: En Unity 2023.1 o más nuevo,
await UniTask.WaitForEndOfFrame();
ya no requiere monobehaviour. UtilizaUnityEngine.Awaitable.EndOfFrameAsync
.
yield return null
y UniTask.Yield
son similares pero diferentes. yield return null
siempre devuelve el siguiente cuadro, pero UniTask.Yield
devuelve el siguiente llamado. Es decir, llame UniTask.Yield(PlayerLoopTiming.Update)
en PreUpdate
, devuelve el mismo marco. UniTask.NextFrame()
garantiza que regrese el siguiente cuadro, puede esperar que esto se comporte exactamente igual que yield return null
.
Unitoask.yield (sin cancelación) es un tipo especial, devuelve
YieldAwaitable
y se ejecuta en el rendimiento. Es el más ligero y más rápido.
AsyncOperation
se devuelve de la sincronización nativa. Por ejemplo, ALEAIT SceneManager.LoadSceneAsync
regresa de EarlyUpdate.UpdatePreloading
y, después de ser llamado, el Start
de la escena cargada se llama desde EarlyUpdate.ScriptRunDelayedStartupFrame
. También await UnityWebRequest
se devuelva de EarlyUpdate.ExecuteMainThreadJobs
.
En AiteSk, espera directamente utiliza el tiempo nativo, mientras que WithCancellation
y ToUniTask
usan el tiempo especificado. Esto generalmente no es un problema en particular, pero con LoadSceneAsync
, causa un orden diferente de inicio y continuación después de esperar. Por lo tanto, se recomienda no usar LoadSceneAsync.ToUniTask
.
Nota: Cuando se use Unity 2023.1 o más nuevo, asegúrese de
using UnityEngine;
En el uso de declaraciones de su archivo cuando se trabaja con nuevos métodosSceneManager.LoadSceneAsync
UnityEngine.Awaitable
Esto evita que los errores de compilación eviten el uso de la versiónUnityEngine.AsyncOperation
.
En StackTrace, puede verificar dónde está funcionando en Playerloop.
De manera predeterminada, el PlayerLoop de UNITEASK se inicializa en [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
.
El orden en el que se llaman los métodos en BefefefeSceneload no es determinista, por lo que si desea usar unitarask en otros métodos de befefefefeneload, debe intentar inicializarlo antes de esto.
// AfterAssembliesLoaded is called before BeforeSceneLoad
[ RuntimeInitializeOnLoadMethod ( RuntimeInitializeLoadType . AfterAssembliesLoaded ) ]
public static void InitUniTaskLoop ( )
{
var loop = PlayerLoop . GetCurrentPlayerLoop ( ) ;
Cysharp . Threading . Tasks . PlayerLoopHelper . Initialize ( ref loop ) ;
}
Si importa el paquete de Entities
de Unity, eso restablece el bucle de reproductor personalizado para predeterminar en BeforeSceneLoad
e inyecta el bucle de ECS. Cuando Unity llama al método de inyección de ECS después del método de inicialización de UNITASK, UNITEASK ya no funcionará.
Para resolver este problema, puede reinicializar el UITEK Playerloop después de que se inicialice ECS.
// Get ECS Loop.
var playerLoop = ScriptBehaviourUpdateOrder . CurrentPlayerLoop ;
// Setup UniTask's PlayerLoop.
PlayerLoopHelper . Initialize ( ref playerLoop ) ;
Puede diagnosticar si el bucle de jugadores de UNITEASK está listo llamando PlayerLoopHelper.IsInjectedUniTaskPlayerLoop()
. Y también PlayerLoopHelper.DumpCurrentPlayerLoop
registra todos los jugadores actuales para consolar.
void Start ( )
{
UnityEngine . Debug . Log ( " UniTaskPlayerLoop ready? " + PlayerLoopHelper . IsInjectedUniTaskPlayerLoop ( ) ) ;
PlayerLoopHelper . DumpCurrentPlayerLoop ( ) ;
}
Puede optimizar ligeramente el costo de bucle eliminando la inyección de USUSE Playerlooptiming. Puede llamar PlayerLoopHelper.Initialize(InjectPlayerLoopTimings)
en Initialize.
var loop = PlayerLoop . GetCurrentPlayerLoop ( ) ;
PlayerLoopHelper . Initialize ( ref loop , InjectPlayerLoopTimings . Minimum ) ; // minimum is Update | FixedUpdate | LastPostLateUpdate
InjectPlayerLoopTimings
tiene tres preajuste, All
y Standard
(todo sin el último excepto lastPostLateUpdate), Minimum
( Update | FixedUpdate | LastPostLateUpdate
). El valor predeterminado es todo y puede combinar tiempos de inyección personalizados como InjectPlayerLoopTimings.Update | InjectPlayerLoopTimings.FixedUpdate | InjectPlayerLoopTimings.PreLateUpdate
.
Puede cometer un error para usar PlayerLoopTiming
no inyectados por microsoft.codeanalysis.bannedapianalyzers. Por ejemplo, puede configurar BannedSymbols.txt
así para 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.
Puede configurar la gravedad RS0030
al error.
async void
es un sistema de tareas C# estándar, por lo que no se ejecuta en los sistemas de Unitoask. Es mejor no usarlo. async UniTaskVoid
es una versión liviana de async UniTask
porque no tiene la finalización esperable e informa los errores de inmediato a UniTaskScheduler.UnobservedTaskException
UnobservedTaskException. Si no necesita esperar (fuego y olvidar), el uso de UniTaskVoid
es mejor. Desafortunadamente, para desestimar la advertencia, debe llamar a Forget()
.
public async UniTaskVoid FireAndForgetMethod ( )
{
// do anything...
await UniTask . Yield ( ) ;
}
public void Caller ( )
{
FireAndForgetMethod ( ) . Forget ( ) ;
}
También AiteSk tiene el método Forget
, es similar a UniTaskVoid
y tiene los mismos efectos. Sin embargo, UniTaskVoid
es más eficiente si no usa por completo await
。
public async UniTask DoAsync ( )
{
// do anything...
await UniTask . Yield ( ) ;
}
public void Caller ( )
{
DoAsync ( ) . Forget ( ) ;
}
Para usar un async lambda registrado en un evento, no use async void
. En su lugar, puede usar UniTask.Action
UniTask.UnityAction
, los cuales crean un delegado a través de 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
también se puede usar en el método Start
de Monobehaviour.
class Sample : MonoBehaviour
{
async UniTaskVoid Start ( )
{
// async init code.
}
}
Útil para verificar (filtradas) unitarias. Puede abrir la ventana del rastreador en Window -> UniTask Tracker
.
UnitaskTracker está destinado al uso de depuración solo, ya que habilitar el seguimiento y la captura de pilas es útil, pero tiene un gran impacto de rendimiento. El uso recomendado es habilitar tanto el seguimiento como las pilas para encontrar fugas de tareas y deshabilitarlas cuando se realicen.
De manera predeterminada, Unitask admite TextMeshPro ( BindTo(TMP_Text)
y TMP_InputField
extensiones de eventos como ugui InputField
estándar), Dotween ( Tween
como esperable) y direccionable ( AsyncOperationHandle
y AsyncOperationHandle<T>
como esperable).
Se definen en ASMDEF separados como UniTask.TextMeshPro
, UniTask.DOTween
, UniTask.Addressables
.
TextMeshPro y el soporte de direccionable se habilitan automáticamente al importar sus paquetes desde Package Manager. Sin embargo, para el soporte entre dos, después de importar desde los activos entre los activos y definir el símbolo de la secuencia de comandos UNITASK_DOTWEEN_SUPPORT
para habilitarlo.
// 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 ) ) ;
El comportamiento predeterminado del apoyo ( await
, WithCancellation
, ToUniTask
) espera que se mata a Tween. Funciona tanto en completo (verdadero/falso) como en matar (verdadero/falso). Pero si desea reutilizar los preadolescentes ( SetAutoKill(false)
), no funciona como se esperaba. Si desea esperar para otro momento, existen los siguientes métodos de extensión en Tween, AwaitForComplete
que lo complejen, AwaitForPause
AwaitForPlay
, AwaitForRewind
AwaitForStepComplete
.
Unity 2020.2 admite C# 8.0 para que pueda usar await foreach
. Esta es la nueva notación de actualización en la era Async.
// Unity 2020.2, C# 8.0
await foreach ( var _ in UniTaskAsyncEnumerable . EveryUpdate ( ) . WithCancellation ( token ) )
{
Debug . Log ( " Update() " + Time . frameCount ) ;
}
En un entorno C# 7.3, puede usar el método ForEachAsync
para trabajar casi de la misma manera.
// C# 7.3(Unity 2018.3~)
await UniTaskAsyncEnumerable . EveryUpdate ( ) . ForEachAsync ( _ =>
{
Debug . Log ( " Update() " + Time . frameCount ) ;
} , token ) ;
UniTask.WhenEach
que es similar a Task.WhenEach
de .NET 9. Cuando eleach puede consumir una nueva forma para esperar múltiples tareas.
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 ( ) ) ;
}
Unitoaskasyncenumerable implementa Linq asíncrono, similar a Linq en IEnumerable<T>
o RX en IObservable<T>
. Todos los operadores de consultas LINQ estándar se pueden aplicar a las transmisiones asincrónicas. Por ejemplo, el siguiente código muestra cómo aplicar un filtro Where a una transmisión asincrónica de clic en un botón que se ejecuta una vez cada dos clics.
await okButton . OnClickAsAsyncEnumerable ( ) . Where ( ( x , i ) => i % 2 == 0 ) . ForEachAsync ( _ =>
{
} ) ;
Fire y olvida el estilo (por ejemplo, manejo de eventos), también puede usar Subscribe
.
okButton . OnClickAsAsyncEnumerable ( ) . Where ( ( x , i ) => i % 2 == 0 ) . Subscribe ( _ =>
{
} ) ;
Async Linq está habilitado cuando using Cysharp.Threading.Tasks.Linq;
, y UniTaskAsyncEnumerable
se define en UniTask.Linq
asmdef.
Está más cerca de UNIRX (extensiones reactivas), pero Unitoaskasyncenumerable es una corriente asincrónica basada en push, mientras que RX era una corriente asincrónica a base de empuje. Tenga en cuenta que, aunque similares, las características son diferentes y los detalles se comportan de manera diferente junto con ellas.
UniTaskAsyncEnumerable
es el punto de entrada como Enumerable
. Además de los operadores de consulta estándar, hay otros generadores para la unidad como EveryUpdate
, Timer
, TimerFrame
, Interval
, IntervalFrame
y EveryValueChanged
. Y también agregó operadores de consultas originales de Unitask adicionales como Append
, Prepend
, DistinctUntilChanged
, ToHashSet
, Buffer
, CombineLatest
, Merge
Do
, Never
, ForEachAsync
, Pairwise
, Publish
, Queue
, Return
, SkipUntil
, TakeUntil
, SkipUntilCanceled
, TakeUntilCanceled
, TakeLast
, Subscribe
.
El método con FUNC como argumento tiene tres sobrecargas adicionales, ***Await
, ***AwaitWithCancellation
.
Select ( Func < T , TR > selector )
SelectAwait ( Func < T , UniTask < TR > > selector )
SelectAwaitWithCancellation ( Func < T , CancellationToken , UniTask < TR > > selector )
Si desea usar el método async
dentro del func, use el ***Await
o ***AwaitWithCancellation
.
Cómo crear un iterador Async: C# 8.0 admite Async Iterator ( async yield return
), pero solo permite IAsyncEnumerable<T>
y, por supuesto, requiere C# 8.0. Unitoask admite UniTaskAsyncEnumerable.Create
método para crear iterador async personalizado.
// 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 ( ) ;
}
} ) ;
}
Todos los componentes UGUI implementos ***AsAsyncEnumerable
para convertir corrientes asíncronas de eventos.
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. " ) ;
}
Todos los eventos de mensajes de Monobehaviour pueden convertir async-streams por AsyncTriggers
que se pueden habilitar using Cysharp.Threading.Tasks.Triggers;
. Asynctrigger se puede crear utilizando GetAsync***Trigger
y se desencadena como unitoaskasyncenumerable.
var trigger = this . GetOnCollisionEnterAsyncHandler ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
// every moves.
await this . GetAsyncMoveTrigger ( ) . ForEachAsync ( axisEventData =>
{
} ) ;
AsyncReactiveProperty
, AsyncReadOnlyReactiveProperty
es la versión de ReactiveProperty de UNITEASK. Método de extensión BindTo
de IUniTaskAsyncEnumerable<T>
para unir valores de flujo asíncrono a los componentes de Unity (text/selectable/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 ) ;
Una transmisión asincrónica de tipo extracción no obtiene los siguientes valores hasta que se complete el procesamiento asíncrono en la secuencia. Esto podría derramar datos de eventos de tipo push, como botones.
// can not get click event during 3 seconds complete.
await button . OnClickAsAsyncEnumerable ( ) . ForEachAwaitAsync ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
Es útil (prevenir doble clic) pero a veces no es útil.
El uso del método Queue()
también colaará eventos durante el procesamiento asincrónico.
// queued message in asynchronous processing
await button . OnClickAsAsyncEnumerable ( ) . Queue ( ) . ForEachAwaitAsync ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
O use Subscribe
, disparar y olvidar el estilo.
button . OnClickAsAsyncEnumerable ( ) . Subscribe ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
Channel
es el mismo que System.Threading.Tasks.Cannels, que es similar a un canal Golang.
Actualmente solo admite canales ilimitados de un solo consumidor de productores múltiples. Puede crear por Channel.CreateSingleConsumerUnbounded<T>()
.
Para el productor ( .Writer
), use TryWrite
para impulsar el valor y TryComplete
para completar el canal. Para Consumer ( .Reader
), use TryRead
, WaitToReadAsync
, ReadAsync
, Completion
y ReadAllAsync
para leer mensajes en cola.
ReadAllAsync
devuelve IUniTaskAsyncEnumerable<T>
por lo que consulta los operadores de Linq. El lector solo permite un solo consumidor, pero usa el operador de consultas .Publish()
para habilitar el mensaje de multidifusión. Por ejemplo, haga pub/subtitilidad.
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 presenta el tipo esperable, espera. En pocas palabras, la esperable puede considerarse un subconjunto de ANITEK, y de hecho, el diseño esperable fue influenciado por unitarask. Debería ser capaz de manejar los espera, tareas agrupadas basadas en Playerloop, tareas agrupadas y soporte para la cancelación con CancellationToken
de manera similar. Con su inclusión en la biblioteca estándar, es posible que se pregunte si continúa usando unitask o migra a esperanza. Aquí hay una breve guía.
Primero, la funcionalidad proporcionada por ATEAITABLE es equivalente a lo que ofrecen las coroutinas. En lugar de yield return
, usa espera; await NextFrameAsync()
reemplaza yield return null
; Y hay equivalentes para WaitForSeconds
y EndOfFrame
. Sin embargo, ese es el alcance de ello. Al estar basado en la coroutina en términos de funcionalidad, carece de características basadas en tareas. En el desarrollo práctico de aplicaciones utilizando async/espera, las operaciones como WhenAll
son esenciales. Además, AiteSk permite muchas operaciones basadas en marco (como DelayFrame
) y un control de jugador más flexible, que no están disponibles en espera. Por supuesto, tampoco hay ventana de rastreador.
Por lo tanto, recomiendo usar unitarask para el desarrollo de aplicaciones. Unitarask es un superconjunto de espera e incluye muchas características esenciales. Para el desarrollo de la biblioteca, donde desea evitar dependencias externas, sería apropiado usar un tipo de retorno para los métodos. La espera puede convertirse a UNITASK utilizando AsUniTask
, por lo que no hay ningún problema en el manejo de la funcionalidad esperable dentro de la biblioteca de Aitarask. Por supuesto, si no necesita preocuparse por las dependencias, el uso de AiteSk sería la mejor opción incluso para el desarrollo de la biblioteca.
El atributo [UnityTest]
de Unity puede probar Coroutine (IEnumerator) pero no puede probar Async. UniTask.ToCoroutine
puentes async/espera a la coroutina para que pueda probar los métodos de async.
[ 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 ;
}
} ) ;
Las propias pruebas unitarias de Unitask se escriben utilizando un corredor de prueba Unity y cysharp/runtimeunittesttoolkit para integrarse con CI y verificar si IL2CPP está funcionando.
La UniTask.SwitchToThreadPool
de los métodos de Unitask se ejecutan en un solo hilo (Playerloop), con solo UniTask.Run
( Task.Run
. Si usa un grupo de hilos, no funcionará con WebGL, etc.
UniTask.Run
ahora está en desuso. Puede usar UniTask.RunOnThreadPool
en su lugar. Y también considere si puede usar UniTask.Create
o UniTask.Void
.
Puede convertir Coroutine (IEnumerator) en AiteSk (o esperar directamente) pero tiene algunas limitaciones.
WaitForEndOfFrame
/ WaitForFixedUpdate
/ Coroutine
no es compatible.Update
que StartCoroutine
, utiliza el PlayerLoopTiming
especificado y el jugador StartCoroutine
PlayerLoopTiming.Update
predeterminado. Si desea una conversión totalmente compatible de Coroutine a Async, use la sobrecarga de IEnumerator.ToUniTask(MonoBehaviour coroutineRunner)
. Se ejecuta StartCoroutine en una instancia del argumento Monobehaviour y espera a que se complete en AiteSk.
Unitask puede ejecutarse en Unity Editor como un Coroutine de editor. Sin embargo, hay algunas limitaciones.
DelayType.Realtime
que espere el momento adecuado.EditorApplication.update
sincronización.-batchmode
With -quit
no funciona porque Unity no ejecuta EditorApplication.update
y renuncia después de un solo cuadro. En su lugar, no use -quit
y renuncie manualmente con EditorApplication.Exit(0)
. ATEKASK tiene muchas API estándar similares a la tarea. Esta tabla muestra cuáles son las API alternativas.
Use el tipo estándar.
Tipo de .NET | Tipo de unaitar |
---|---|
IProgress<T> | --- |
CancellationToken | --- |
CancellationTokenSource | --- |
Utilice el tipo de Unitoask.
Tipo de .NET | Tipo de unaitar |
---|---|
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 almacena agresivamente los objetos de Async Promise para lograr la asignación cero (para detalles técnicos, consulte la publicación del blog Aitok v2 - asíncape cero async/espera para la unidad, con Linq asincrónico). De manera predeterminada, almacena en caché todas las promesas, pero puede configurar TaskPool.SetMaxPoolSize
a su valor, el valor indica el tamaño de la caché por tipo. TaskPool.GetCacheSizeInfo
devuelve actualmente objetos en caché en el grupo.
foreach ( var ( type , size ) in TaskPool . GetCacheSizeInfo ( ) )
{
Debug . Log ( type + " : " + size ) ;
}
En UnityEditor, el Profiler muestra la asignación de AsyncStatemachine generada por el compilador, pero solo ocurre en la construcción de depuración (desarrollo). El compilador C# genera asyncstatemachine como clase en la construcción de depuración y como estructura en la compilación de lanzamiento.
Unity admite la opción de optimización del código a partir de 2020.1 (derecha, pie de página).
Puede cambiar la optimización del compilador de C# para liberar para eliminar la asignación de AsyncStateMachine en las compilaciones de desarrollo. Esta opción de optimización también se puede establecer a través de Compilation.CompilationPipeline-codeOptimization
y Compilation.CodeOptimization
.
La sincronización predeterminada de Unity ( UnitySynchronizationContext
) es una implementación deficiente para el rendimiento. Unitask omite SynchronizationContext
(y ExecutionContext
) para que no lo use, pero si existe en async Task
, todavía la usa. UniTaskSynchronizationContext
es un reemplazo de UnitySynchronizationContext
que es mejor para el rendimiento.
public class SyncContextInjecter
{
[ RuntimeInitializeOnLoadMethod ( RuntimeInitializeLoadType . SubsystemRegistration ) ]
public static void Inject ( )
{
SynchronizationContext . SetSynchronizationContext ( new UniTaskSynchronizationContext ( ) ) ;
}
}
Esta es una opción opcional y no siempre se recomienda; UniTaskSynchronizationContext
es menos perfilante que async UniTask
y no es un reemplazo completo de unaitaria. Tampoco garantiza la compatibilidad de comportamiento completa con el UnitySynchronizationContext
.
Las referencias de API de Unitask están alojadas en cysharp.github.io/unitask por DOCFX y CYSHARP/DOCFXTEMPLATE.
Por ejemplo, los métodos de fábrica de Unitask se pueden ver en los métodos de UNITASK#. Los métodos de fábrica/extensión de Unitaskasyncenumerable se pueden ver en las tareas#de laskasyncenumerables.
Requiere una versión de Unity que admite el parámetro de consulta de ruta para los paquetes GIT (Unity> = 2019.3.4f1, Unity> = 2020.1A21). Puede agregar https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
a paquetes administradores
O agregue "com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"
a Packages/manifest.json
.
Si desea establecer una versión de destino, UITEK usa la etiqueta de lanzamiento *.*.*
Para que pueda especificar una versión como #2.1.0
. Por ejemplo, https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0
.
Para .NET Core, use Nuget.
PM> Instalación de paquetes a unaitar
Unitok de .NET Core Version es un subconjunto de Unity Aityask con métodos dependientes de Playerloop eliminados.
Se ejecuta a un rendimiento más alto que la tarea/valueTask estándar, pero debe tener cuidado de ignorar el ExecutionContext/SynchronizationContext al usarla. AsyncLocal
tampoco funciona porque ignora ExecutionContext.
Si usa unitarask internamente, pero proporciona valueTask como una API externa, puede escribirla como lo siguiente (inspirado en PoolDawait).
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 La versión está destinada a permitir que los usuarios usen unitask como una interfaz al compartir código con Unity (como Cysharp/MagicOnion). .NET Core Version de AiteSk habilita el intercambio de código suave.
Los métodos de utilidad, como cuándo todos los que son equivalentes a laitarasa, se proporcionan como cysharp/valueTaskSuplement.
Esta biblioteca está bajo la licencia MIT.