Fornece uma alocação eficiente de integração assíncrona/aguarda para unidade.
UniTask<T>
e ASYNCMETHODBUILDER CUDDADO para obter alocação zeroUniTask.Yield
, UniTask.Delay
, UniTask.DelayFrame
, etc.) que permitem substituir todas as operações de coroutina Para detalhes técnicos, consulte Postagem do blog: UnitaSk V2 - ASYNC/Aguarda de alocação zero/Aguarda para a Unidade, com Linq assíncrono
Para dicas avançadas, consulte o blog Posta
Instale via pacote UPM com o pacote de referência Git ou ativo ( UniTask.*.*.*.unitypackage
) disponível no UnitaSk/Releases.
// 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 " ) ;
}
Os recursos do UnitaSk dependem do recurso C# 7.0 (recurso de construtor de métodos de métodos personalizados do tipo tarefa) para que a versão do Unity necessária seja após Unity 2018.3
, a versão mais baixa oficial suportada é Unity 2018.4.13f1
.
Por que o UnitaSk (objeto de tarefa personalizado) é necessário? Porque a tarefa é muito pesada e não corresponde ao encadeamento da unidade (thread único). O UnitaSk não usa threads e SynchronizationContext/ExecutionContext porque o objeto assíncrono da Unity é automático despachado pela camada do motor da Unity. Ele alcança uma alocação mais rápida e inferior e é completamente integrada à unidade.
Você pode aguardar AsyncOperation
, ResourceRequest
, AssetBundleRequest
, AssetBundleCreateRequest
, UnityWebRequestAsyncOperation
, AsyncGPUReadbackRequest
, IEnumerator
e outros ao using Cysharp.Threading.Tasks;
.
O UnitaSk fornece três métodos de padrão de extensão.
* await asyncOperation ;
* . WithCancellation ( CancellationToken ) ;
* . ToUniTask ( IProgress , PlayerLoopTiming , CancellationToken ) ;
WithCancellation
é uma versão simples do ToUniTask
, ambos retornam UniTask
. Para detalhes do cancelamento, consulte: Seção de cancelamento e manipulação de exceções.
NOTA: Aguarda diretamente é devolvido do tempo nativo do Playerloop, mas com o componente e o Tounitask são devolvidos do PlayerLootiming especificado. Para detalhes do tempo, consulte: Seção Playerloop.
Nota: AssetBundleRequest tem
asset
eallAssets
, o padrão aguardaasset
de devolução. Se você deseja obterallAssets
, pode usar o métodoAwaitForAllAssets()
.
O tipo de UniTask
pode usar utilitários como UniTask.WhenAll
, UniTask.WhenAny
, UniTask.WhenEach
. Eles são como Task.WhenAll
Task.WhenAny
Eles retornam o valor de tuplas para que você possa desconstruir cada resultado e passar vários 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 ) ;
}
Se você deseja converter um retorno de chamada para o UnitaSk, pode usar UniTaskCompletionSource<T>
, que é uma edição leve do 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>
}
Você pode converter tarefa -> UnitaSk: AsUniTask
, UniTask
-> UniTask<AsyncUnit>
: AsAsyncUnitUniTask
, UniTask<T>
-> UniTask
: AsUniTask
. UniTask<T>
-> O custo de conversão de UniTask
é gratuito.
Se você deseja converter o Async em coroutina, pode usar .ToCoroutine()
, isso é útil se você deseja permitir apenas o uso do sistema de coroutine.
UnitaSk não pode aguardar duas vezes. Esta é uma restrição semelhante ao Valuetask/IValuetaskSource introduzido no .NET Standard 2.1.
As seguintes operações nunca devem ser executadas em uma instância do Valuetask:
- Aguardando a instância várias vezes.
- Chamando Astask várias vezes.
- Usando .result ou .getAwaiter (). GetResult () quando a operação ainda não foi concluída ou usá -los várias vezes.
- Usando mais de uma dessas técnicas para consumir a instância.
Se você fizer alguma das opções acima, os resultados serão indefinidos.
var task = UniTask . DelayFrame ( 10 ) ;
await task ;
await task ; // NG, throws Exception
Armazene no campo da classe, você pode usar UniTask.Lazy
que suporta ligar várias vezes. .Preserve()
permite várias chamadas (resultados internos em cache). Isso é útil quando existem várias chamadas em um escopo de função.
Além disso, UniTaskCompletionSource
pode aguardar várias vezes e aguardar de muitos chamadores.
Alguns métodos de fábrica UnitaSk possuem um CancellationToken cancellationToken = default
. Além disso, algumas operações assíncronas para a unidade têm WithCancellation(CancellationToken)
e ToUniTask(..., CancellationToken cancellation = default)
Métodos de extensão.
Você pode passar CancellationToken
para o parâmetro por CancellationTokenSource
padrãoTokensource.
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 ) ;
O CancellationToken pode ser criado pelo Método de Extensão CancellationTokenSource
ou Monobehaviour GetCancellationTokenOnDestroy
.
// this CancellationToken lifecycle is same as GameObject.
await UniTask . DelayFrame ( 1000 , cancellationToken : this . GetCancellationTokenOnDestroy ( ) ) ;
Para o cancelamento de propagação, todo o método Async recomendou aceitar CancellationToken cancellationToken
no último argumento e aprovar CancellationToken
de raiz para o fim.
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 assíncrono. Você pode segurar seu próprio ciclo de vida em vez de cancelamento padrão.
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 ( ) ;
}
}
Após a unidade 2022.2, a unidade adiciona cancelamento em monobehaviour.destroycancellationToken e Application.ExitCancellationToken.
Quando o cancelamento é detectado, todos os métodos lançam OperationCanceledException
e se propagam a montante. Quando a exceção (não limitada à OperationCanceledException
) não é tratada no método assíncrono, é propagada finalmente ao UniTaskScheduler.UnobservedTaskException
. O comportamento padrão da exceção não tratada recebida é escrever log como exceção. O nível de log pode ser alterado usando UniTaskScheduler.UnobservedExceptionWriteLogType
. Se você deseja usar o comportamento personalizado, defina uma ação como UniTaskScheduler.UnobservedTaskException.
E também OperationCanceledException
é uma exceção especial, isso é silenciosamente ignorado na UnobservedTaskException
.
Se você deseja cancelar o comportamento em um método Async UnitaSk, jogue manualmente OperationCanceledException
.
public async UniTask < int > FooAsync ( )
{
await UniTask . Yield ( ) ;
throw new OperationCanceledException ( ) ;
}
Se você lidar com uma exceção, mas deseja ignorar (propagar ao manuseio global de cancelamento), use um filtro de exceção.
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 ;
}
}
Os lances/captura OperationCanceledException
são um pouco pesados; portanto, se o desempenho for uma preocupação, use UniTask.SuppressCancellationThrow
para evitar o arremesso de OperaçãoCancEleDexception. Retorna (bool IsCanceled, T Result)
em vez de jogar.
var ( isCanceled , _ ) = await UniTask . DelayFrame ( 10 , cancellationToken : cts . Token ) . SuppressCancellationThrow ( ) ;
if ( isCanceled )
{
// ...
}
NOTA: Somente suprimir os lances se você ligar diretamente para o método mais fonte. Caso contrário, o valor de retorno será convertido, mas todo o pipeline não suprimirá os lances.
Alguns recursos que usam o loop de jogadores da Unity, como UniTask.Yield
e UniTask.Delay
etc, determinam o estado de cancelamento no loop do jogador. Isso significa que ele não cancelou imediatamente após CancellationToken
disparado.
Se você deseja alterar esse comportamento, o cancelamento será imediato, defina a sinalização cancelImmediately
como um argumento.
await UniTask . Yield ( cancellationToken , cancelImmediately : true ) ;
NOTA: A configuração cancelImmediately
para o verdadeiro e a detecção de um cancelamento imediato é mais caro que o comportamento padrão. Isso ocorre porque ele usa CancellationToken.Register
; É mais pesado do que verificar o cancelamento no loop do jogador.
O tempo limite é uma variação do cancelamento. Você pode definir o tempo limite por CancellationTokenSouce.CancelAfterSlim(TimeSpan)
e passar o cancelamento dos métodos assíncronos.
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
é uma API padrão. No entanto, na unidade, você não deve usá -lo porque depende do timer de rosqueamento.CancelAfterSlim
é os métodos de extensão da UnitaSk, ele usa o PlayerLoop.
Se você deseja usar o tempo limite com outra fonte de cancelamento, 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. " ) ;
}
}
Otimize para reduzir a alocação de cancelamentoTokensource para o tempo limite por método Async, você pode usar TimeoutController
do 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 " ) ;
}
}
}
Se você deseja usar o tempo limite com outra fonte de cancelamento, use new TimeoutController(CancellationToken)
.
TimeoutController timeoutController ;
CancellationTokenSource clickCancelSource ;
void Start ( )
{
this . clickCancelSource = new CancellationTokenSource ( ) ;
this . timeoutController = new TimeoutController ( clickCancelSource ) ;
}
NOTA: O CancellationToken
.TimeoutWithoutException
.Timeout
Porque .Timeout
Work from External of Task, não pode interromper a tarefa de tempo de limpeza. .Timeout
significa ignorar o resultado quando o tempo limite. Se você passar por um CancellationToken
do método, ele atuará de dentro da tarefa, por isso é possível interromper uma tarefa em execução.
Algumas operações assíncronas para a unidade têm métodos de extensão 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 ) ;
Você não deve usar new System.Progress<T>
, porque causa alocação sempre. Use Cysharp.Threading.Tasks.Progress
. Esta fábrica de progressos possui dois métodos, Create
e CreateOnlyValueChanged
. CreateOnlyValueChanged
Chamadas somente quando o valor do progresso foi alterado.
A implementação da interface iProgress para o chamador é melhor, pois não há alocação de 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
}
}
O UnitaSk é executado em um PlayerLoop personalizado. Os métodos baseados em PlayerLoop da UnitaSk (como Delay
, DelayFrame
, asyncOperation.ToUniTask
, etc ...) aceitam esse 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 quando executar, você pode verificar o playerlooplist.md para o PlayerLoop padrão do Unity e injetar o loop personalizado do UnitaSk.
PlayerLoopTiming.Update
é semelhante ao yield return null
em uma coroutina, mas é chamado antes da atualização (Atualizar e UGUI Events (Button.OnClick, etc ...) são chamados no ScriptRunBehaviourUpdate
, o retorno de rendimento é chamado no ScriptRunDelayedDynamicFrameRate
). PlayerLoopTiming.FixedUpdate
é semelhante ao WaitForFixedUpdate
.
PlayerLoopTiming.LastPostLateUpdate
não é equivalente aoyield return new WaitForEndOfFrame()
. O WaitFoFofframe da Coroutine parece correr depois que o Playerloop está pronto. Alguns métodos que requerem o final do quadro da Coroutine (Texture2D.ReadPixels
,ScreenCapture.CaptureScreenshotAsTexture
,CommandBuffer
, etc) não funcionam corretamente quando substituídos por assíncronos/aguardar. Nesses casos, passe Monobehaviour (Coroutine Runnner) paraUniTask.WaitForEndOfFrame
. Por exemplo,await UniTask.WaitForEndOfFrame(this);
é a alocação leve alternativa gratuita deyield return new WaitForEndOfFrame()
.NOTA: Na unidade 2023.1 ou mais recente,
await UniTask.WaitForEndOfFrame();
não requer mais monobehaviour. UsaUnityEngine.Awaitable.EndOfFrameAsync
.
yield return null
e UniTask.Yield
são semelhantes, mas diferentes. yield return null
Sempre retorna o próximo quadro, mas UniTask.Yield
retorna em seguida chamado. Isto é, ligue para UniTask.Yield(PlayerLoopTiming.Update)
no PreUpdate
, ele retorna o mesmo quadro. UniTask.NextFrame()
Garante o retorno do próximo quadro, você pode esperar que isso se comporte exatamente o mesmo que yield return null
.
UnitaSk.yield (sem cancelamento) é um tipo especial, retorna
YieldAwaitable
e executa no rendimento. É o mais leve e mais rápido.
AsyncOperation
é retornada do tempo nativo. Por exemplo, aguarde SceneManager.LoadSceneAsync
é devolvido do EarlyUpdate.UpdatePreloading
e, depois de ser chamado, o Start
da cena carregado é chamado de EarlyUpdate.ScriptRunDelayedStartupFrame
. await UnityWebRequest
é devolvido de EarlyUpdate.ExecuteMainThreadJobs
.
No UnitaSk, aguarda diretamente o tempo nativo, enquanto WithCancellation
e ToUniTask
usam o tempo especificado. Isso geralmente não é um problema específico, mas com LoadSceneAsync
, causa uma ordem diferente de início e continuação após aguardar. Portanto, é recomendável não usar LoadSceneAsync.ToUniTask
.
NOTA: Ao usar o Unity 2023.1 ou mais recente, verifique se você tem
using UnityEngine;
nas declarações de uso do seu arquivo ao trabalhar com o novoUnityEngine.Awaitable
SceneManager.LoadSceneAsync
Isso evita erros de compilação, evitando o uso da versãoUnityEngine.AsyncOperation
.
No Stacktrace, você pode verificar onde está sendo executado no PlayerLoop.
Por padrão, o PlayerLoop do Unidadesk é inicializado em [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
.
A ordem em que os métodos são chamados no BeforeSceneload não é não determinista; portanto, se você quiser usar o UnitaSk em outros métodos BeforeSceneload, tente inicializá -lo antes disso.
// AfterAssembliesLoaded is called before BeforeSceneLoad
[ RuntimeInitializeOnLoadMethod ( RuntimeInitializeLoadType . AfterAssembliesLoaded ) ]
public static void InitUniTaskLoop ( )
{
var loop = PlayerLoop . GetCurrentPlayerLoop ( ) ;
Cysharp . Threading . Tasks . PlayerLoopHelper . Initialize ( ref loop ) ;
}
Se você importar o pacote Entities
da Unity, isso redefine o loop de player personalizado para o padrão no BeforeSceneLoad
e injeta o loop da ECS. Quando a Unity chama o método de injeção do ECS após o método Initialize da UnitaSk, o UnitaSk não funcionará mais.
Para resolver esse problema, você pode reinicializar o UnitaSk PlayerLoop após a inicialização do ECS.
// Get ECS Loop.
var playerLoop = ScriptBehaviourUpdateOrder . CurrentPlayerLoop ;
// Setup UniTask's PlayerLoop.
PlayerLoopHelper . Initialize ( ref playerLoop ) ;
Você pode diagnosticar se o loop de players da UnitaSk está pronto ligando para PlayerLoopHelper.IsInjectedUniTaskPlayerLoop()
. E também PlayerLoopHelper.DumpCurrentPlayerLoop
registra todos os players atuais para consolar.
void Start ( )
{
UnityEngine . Debug . Log ( " UniTaskPlayerLoop ready? " + PlayerLoopHelper . IsInjectedUniTaskPlayerLoop ( ) ) ;
PlayerLoopHelper . DumpCurrentPlayerLoop ( ) ;
}
Você pode otimizar um pouco o custo do loop, removendo a injeção não utilizada do PlayerLoOtiming. Você pode ligar para PlayerLoopHelper.Initialize(InjectPlayerLoopTimings)
no Initialize.
var loop = PlayerLoop . GetCurrentPlayerLoop ( ) ;
PlayerLoopHelper . Initialize ( ref loop , InjectPlayerLoopTimings . Minimum ) ; // minimum is Update | FixedUpdate | LastPostLateUpdate
InjectPlayerLoopTimings
possui três predefinições, All
e Standard
(tudo sem o último, exceto o lastPostLateUpdate), Minimum
( Update | FixedUpdate | LastPostLateUpdate
). O padrão é tudo e você pode combinar horários de injeção personalizada como InjectPlayerLoopTimings.Update | InjectPlayerLoopTimings.FixedUpdate | InjectPlayerLoopTimings.PreLateUpdate
.
Você pode cometer erros para usar PlayerLoopTiming
sem injeção por Microsoft.codeanalysis.bangapianalyzers. Por exemplo, você pode configurar BannedSymbols.txt
como este 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.
Você pode configurar a gravidade RS0030
para o erro.
async void
é um sistema de tarefa C# padrão, para que não seja executado nos sistemas UnitaSk. É melhor não usá -lo. async UniTaskVoid
é uma versão leve do async UniTask
porque não tem conclusão aguardável e relata erros imediatamente ao UniTaskScheduler.UnobservedTaskException
. Se você não precisar de aguardar (fogo e esquecer), o uso UniTaskVoid
é melhor. Infelizmente, para descartar o aviso, você deve ligar Forget()
.
public async UniTaskVoid FireAndForgetMethod ( )
{
// do anything...
await UniTask . Yield ( ) ;
}
public void Caller ( )
{
FireAndForgetMethod ( ) . Forget ( ) ;
}
Além disso, o UnitaSk tem o método Forget
, é semelhante ao UniTaskVoid
e tem os mesmos efeitos. No entanto, UniTaskVoid
é mais eficiente se você não usar completamente, await
。
public async UniTask DoAsync ( )
{
// do anything...
await UniTask . Yield ( ) ;
}
public void Caller ( )
{
DoAsync ( ) . Forget ( ) ;
}
Para usar um lambda assíncrono registrado em um evento, não use async void
. Em vez disso, você pode usar UniTask.Action
ou UniTask.UnityAction
, os quais criam um delegado via 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
também pode ser usado no método Start
de Monobehaviour.
class Sample : MonoBehaviour
{
async UniTaskVoid Start ( )
{
// async init code.
}
}
Útil para verificar (vazados) unidades. Você pode abrir a janela do rastreador na Window -> UniTask Tracker
.
O UnitaSkTracker destina -se a depurar o uso apenas como permitir o rastreamento e a captura de tracas de empilhamento é útil, mas tem um impacto de desempenho pesado. O uso recomendado é permitir que o rastreamento e o Stacktraces encontre vazamentos de tarefas e desative -os quando terminar.
Por padrão, o UnitaSk suporta TextMeshPro ( BindTo(TMP_Text)
e TMP_InputField
Event Extensions como padrão UGUI InputField
), Dotween ( Tween
como aguardável) e endereços ( AsyncOperationHandle
e AsyncOperationHandle<T>
como esperado).
Existem definidas em ASMDEFs separados como UniTask.TextMeshPro
, UniTask.DOTween
, UniTask.Addressables
.
O suporte a TextMeshPro e endereços são ativados automaticamente ao importar seus pacotes do gerenciador de pacotes. No entanto, para o suporte, depois de importar dos ativos entre os ativos e definir o script, defina símbolo UNITASK_DOTWEEN_SUPPORT
para ativá -lo.
// 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 ) ) ;
O comportamento padrão do apoio do apoio ( await
, WithCancellation
, ToUniTask
) aguarda entre a entrega. Funciona em completo (verdadeiro/falso) e matar (verdadeiro/falso). Mas se você deseja reutilizar adolescentes ( SetAutoKill(false)
), ele não funciona conforme o esperado. Se você deseja aguardar outro momento, os seguintes métodos de extensão existem em Tween, aguardam o AwaitForComplete
, AwaitForPause
, aguarde AwaitForPlay
, AwaitForRewind
, aguarde, AwaitForStepComplete
.
A Unidade 2020.2 suporta C# 8.0 para que você possa usar await foreach
. Esta é a nova notação de atualização na era assíncrona.
// Unity 2020.2, C# 8.0
await foreach ( var _ in UniTaskAsyncEnumerable . EveryUpdate ( ) . WithCancellation ( token ) )
{
Debug . Log ( " Update() " + Time . frameCount ) ;
}
Em um ambiente C# 7.3, você pode usar o método ForEachAsync
para trabalhar quase da mesma maneira.
// C# 7.3(Unity 2018.3~)
await UniTaskAsyncEnumerable . EveryUpdate ( ) . ForEachAsync ( _ =>
{
Debug . Log ( " Update() " + Time . frameCount ) ;
} , token ) ;
UniTask.WhenEach
que o que é semelhante à Task.WhenEach
do .NET 9. Quando o que pode consumir uma nova maneira de aguardar várias tarefas.
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 ( ) ) ;
}
UnitakasyceNumerable implementa o LINQ assíncrono, semelhante ao LINQ em IEnumerable<T>
ou rx em IObservable<T>
. Todos os operadores de consulta LINQ padrão podem ser aplicados a fluxos assíncronos. Por exemplo, o código a seguir mostra como aplicar um filtro onde um fluxo assíncrono de clique com o botão que é executado uma vez a cada dois cliques.
await okButton . OnClickAsAsyncEnumerable ( ) . Where ( ( x , i ) => i % 2 == 0 ) . ForEachAsync ( _ =>
{
} ) ;
Fire e esqueça o estilo (por exemplo, manuseio de eventos), você também pode usar Subscribe
.
okButton . OnClickAsAsyncEnumerable ( ) . Where ( ( x , i ) => i % 2 == 0 ) . Subscribe ( _ =>
{
} ) ;
O Async Linq é ativado ao using Cysharp.Threading.Tasks.Linq;
e UniTaskAsyncEnumerable
é definido no UniTask.Linq
asmdef.
É mais próximo do UNIRX (extensões reativas), mas o Unitakasycenumerable é um fluxo assíncrono baseado em tração, enquanto o RX foi um fluxo assíncrono baseado em push. Observe que, embora semelhante, as características são diferentes e os detalhes se comportam de maneira diferente junto com eles.
UniTaskAsyncEnumerable
é o ponto de entrada como Enumerable
. Além dos operadores de consulta padrão, existem outros geradores para unidade, como EveryUpdate
, Timer
, TimerFrame
, Interval
, IntervalFrame
e EveryValueChanged
. E também adicionou operadores de consulta Origterizk adicionais, como Append
, Prepend
, DistinctUntilChanged
, ToHashSet
, Buffer
, CombineLatest
, Merge
Do
Subscribe
Never
, ForEachAsync
, Pairwise
, Publish
, Queue
, Return
, SkipUntil
, TakeUntil
, SkipUntilCanceled
, TakeUntilCanceled
, TakeLast
.
O método com Func como argumento tem três sobrecargas adicionais, ***Await
, ***AwaitWithCancellation
.
Select ( Func < T , TR > selector )
SelectAwait ( Func < T , UniTask < TR > > selector )
SelectAwaitWithCancellation ( Func < T , CancellationToken , UniTask < TR > > selector )
Se você deseja usar o método async
dentro do Func, use o ***Await
ou ***AwaitWithCancellation
.
Como criar um iterador assíncrono: C# 8.0 suporta o iterador assíncrono ( async yield return
), mas apenas permite IAsyncEnumerable<T>
e, é claro, requer C# 8.0. O UnitaSk suporta o método UniTaskAsyncEnumerable.Create
para criar o iterador assíncrono 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 ( ) ;
}
} ) ;
}
Todo o componente UGUI implementa ***AsAsyncEnumerable
para converter fluxos assíncronos 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 os eventos de mensagem Monobehaviour podem converter streams assíncronos por AsyncTriggers
que podem ser ativados using Cysharp.Threading.Tasks.Triggers;
. O assíncilado pode ser criado usando GetAsync***Trigger
e gatilho como unidadeskasyncenumerable.
var trigger = this . GetOnCollisionEnterAsyncHandler ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
// every moves.
await this . GetAsyncMoveTrigger ( ) . ForEachAsync ( axisEventData =>
{
} ) ;
AsyncReactiveProperty
, AsyncReadOnlyReactiveProperty
é a versão do ReactiveProperty da UnitaSk. Método de extensão BindTo
de IUniTaskAsyncEnumerable<T>
para vincular valores de fluxo assíncrono a componentes da unidade (Text/Selectable/TMP/Texto).
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 ) ;
Um fluxo assíncrono do tipo tração não obtém os próximos valores até que o processamento assíncrono na sequência esteja concluído. Isso pode derramar dados de eventos do tipo push, como botões.
// can not get click event during 3 seconds complete.
await button . OnClickAsAsyncEnumerable ( ) . ForEachAwaitAsync ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
É útil (evite clique duas vezes), mas não é útil às vezes.
O uso do método Queue()
também fará fila de eventos durante o processamento assíncrono.
// queued message in asynchronous processing
await button . OnClickAsAsyncEnumerable ( ) . Queue ( ) . ForEachAwaitAsync ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
Ou use Subscribe
, disparar e esquecer o estilo.
button . OnClickAsAsyncEnumerable ( ) . Subscribe ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
Channel
é o mesmo que System.Threading.Tasks.Channels, que é semelhante a um canal de Golang.
Atualmente, ele suporta apenas canais ilimitados de múltiplos produtores e consumidores únicos. Ele pode criar por Channel.CreateSingleConsumerUnbounded<T>()
.
Para o produtor ( .Writer
), use TryWrite
para pressionar o valor e TryComplete
para concluir o canal. Para o consumidor ( .Reader
), use TryRead
, WaitToReadAsync
, ReadAsync
, Completion
e ReadAllAsync
para ler mensagens na fila.
ReadAllAsync
retorna IUniTaskAsyncEnumerable<T>
SO, consulte os operadores LINQ. O leitor permite apenas consumidor único, mas usa o operador de consulta .Publish()
para ativar a mensagem multicast. Por exemplo, faça o pub/subtilidade.
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 ( ) ;
}
}
A Unidade 6 apresenta o tipo aguardável, aguardável. Simplificando, aguardável pode ser considerado um subconjunto de UnitaSk e, de fato, o design da Aguardable foi influenciado pelo UnitaSk. Ele deve ser capaz de lidar com o PlayerLoop aguarda, tarefas agrupadas e suporte para cancelamento com CancellationToken
de maneira semelhante. Com sua inclusão na biblioteca padrão, você pode se perguntar se continuar usando o UnitaSk ou migrar para aguardar. Aqui está um breve guia.
Primeiro, a funcionalidade fornecida pela Aguardável é equivalente ao que os Coroutines oferecem. Em vez de yield return
, você usa aguarda; await NextFrameAsync()
substitui yield return null
; E existem equivalentes para WaitForSeconds
e EndOfFrame
. No entanto, essa é a extensão disso. Sendo baseado em coroutina em termos de funcionalidade, não possui recursos baseados em tarefas. No desenvolvimento prático de aplicações usando async/aguardar, operações como WhenAll
são essenciais. Além disso, o UnitaSk permite muitas operações baseadas em quadros (como DelayFrame
) e o controle mais flexível do PlayerLootiming, que não estão disponíveis para aguardar. Claro, também não há janela do rastreador.
Portanto, recomendo usar o UnitaSk para o desenvolvimento de aplicativos. O UnitaSk é um superconjunto aguardável e inclui muitos recursos essenciais. Para o desenvolvimento da biblioteca, onde você deseja evitar dependências externas, o uso aguardável como tipo de retorno para métodos seria apropriado. Aguardável pode ser convertido em UnitaSk usando AsUniTask
, portanto, não há problema no manuseio da funcionalidade baseada na biblioteca do UnitaSk. Obviamente, se você não precisar se preocupar com dependências, o uso do UnitaSk seria a melhor escolha, mesmo para o desenvolvimento da biblioteca.
O atributo [UnityTest]
da Unity pode testar a Coroutine (Ienumerator), mas não pode testar aspíadas. UniTask.ToCoroutine
Bridges Async/Wait to Coroutine para que você possa testar os métodos assíncronos.
[ 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 ;
}
} ) ;
Os próprios testes de unidade do UnitaSk são gravados usando o Unity Test Runner e o CySharp/RunTimeUnittestToolkit para se integrar ao IC e verificar se o IL2CPP está funcionando.
A maioria dos métodos de unidadek é executada em um único thread (playerloop), com apenas UniTask.Run
( Task.Run
equivalente) e UniTask.SwitchToThreadPool
em execução em um pool de threads. Se você usar um pool de threads, ele não funcionará com o WebGL e assim por diante.
UniTask.Run
agora está preguiçoso. Você pode usar UniTask.RunOnThreadPool
. E também considere se você pode usar UniTask.Create
ou UniTask.Void
.
Você pode converter a Coroutine (Ienumerator) em UnitaSk (ou aguardar diretamente), mas possui algumas limitações.
WaitForEndOfFrame
/ WaitForFixedUpdate
/ Coroutine
não é suportado.StartCoroutine
, ele usa o PlayerLoopTiming
especificado e o padrão PlayerLoopTiming.Update
é executado antes Update
do Monobehaviour e o loop da StartCoroutine
. Se você deseja uma conversão totalmente compatível de coroutina para assíncrona, use a sobrecarga IEnumerator.ToUniTask(MonoBehaviour coroutineRunner)
. Ele executa o StartCoroutine em uma instância do argumento monobehaviour e aguarda a conclusão do UnitaSk.
O UnitaSk pode ser executado no editor de unidades como uma corota do editor. No entanto, existem algumas limitações.
DelayType.Realtime
que aguarda o tempo certo.EditorApplication.update
.-batchmode
com -quit
não funciona porque a unidade não executa EditorApplication.update
e sai após um único quadro. Em vez disso, não use -quit
e pare manualmente com EditorApplication.Exit(0)
. O UnitaSk possui muitas APIs padrão de tarefas. Esta tabela mostra quais são as APIs alternativas.
Use o tipo padrão.
.NET TIPO | Tipo de unidade |
---|---|
IProgress<T> | ---- |
CancellationToken | ---- |
CancellationTokenSource | ---- |
Use o tipo UnitaSk.
.NET TIPO | Tipo de unidade |
---|---|
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 |
O UnitaSk cache agressivamente o ASYNC Promove Objetos para obter alocação zero (para detalhes técnicos, consulte o blog Post UnitaSk V2 - ASYNC/Aguarda de alocação zero/Aguarda para a unidade, com o LINQ assíncrono). Por padrão, ele armazena em cache todas as promessas, mas você pode configurar TaskPool.SetMaxPoolSize
para o seu valor, o valor indica o tamanho do cache por tipo. TaskPool.GetCacheSizeInfo
retorna os objetos em cache atualmente no pool.
foreach ( var ( type , size ) in TaskPool . GetCacheSizeInfo ( ) )
{
Debug . Log ( type + " : " + size ) ;
}
No UnityEditor, o Profiler mostra a alocação do compilador gerado como asyncstatemachine, mas ocorre apenas na construção de depuração (desenvolvimento). O compilador C# gera asyncstatemachine como classe na construção de depuração e como estrutura na construção de liberação.
O Unity suporta a opção de otimização de código a partir de 2020.1 (direita, rodapé).
Você pode alterar a otimização do compilador C# para liberar para remover a alocação do AsyncStatemachine nas compilações de desenvolvimento. Essa opção de otimização também pode ser definida via Compilation.CompilationPipeline-codeOptimization
e Compilation.CodeOptimization
.
A sincronização padrão da Unity ( UnitySynchronizationContext
) é uma má implementação para o desempenho. O UnitaSk ignora SynchronizationContext
(e ExecutionContext
) para que não o use, mas se existe na async Task
, ainda a usou. UniTaskSynchronizationContext
é uma substituição do UnitySynchronizationContext
, que é melhor para o desempenho.
public class SyncContextInjecter
{
[ RuntimeInitializeOnLoadMethod ( RuntimeInitializeLoadType . SubsystemRegistration ) ]
public static void Inject ( )
{
SynchronizationContext . SetSynchronizationContext ( new UniTaskSynchronizationContext ( ) ) ;
}
}
Esta é uma escolha opcional e nem sempre é recomendada; UniTaskSynchronizationContext
é menos executado que async UniTask
e não é uma substituição completa do UnitaSk. Também não garante compatibilidade comportamental completa com o UnitySynchronizationContext
.
As referências da API do UnitaSk estão hospedadas em cysharp.github.io/unitak por docfx e cysharp/docfxtemplate.
Por exemplo, os métodos de fábrica da UnitaSk podem ser vistos nos métodos UnitaSk#. Os métodos de fábrica/extensão da UnitaSkasyceNumerable podem ser vistos em métodos#UnitaSkasyceNumerable.
Requer uma versão da unidade que suporta o parâmetro de consulta de caminho para pacotes Git (unidade> = 2019.3.4f1, unidade> = 2020.1a21). Você pode adicionar https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
para gerenciador de pacotes
ou adicione "com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"
para Packages/manifest.json
.
Se você deseja definir uma versão de destino, o UnitaSk usa a tag *.*.*
Libere para que você possa especificar uma versão como #2.1.0
. Por exemplo, https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0
.
Para .Net Core, use o NUGET.
PM> Install-Package UnitaSk
UnitaSk of .Net Core Version é um subconjunto de unidade unidade com os métodos dependentes do playerloop removidos.
Ele é executado com maior desempenho que a tarefa/ValuETask padrão, mas você deve ter cuidado para ignorar o ExecutionContext/SynchronizationContext ao usá -lo. AsyncLocal
também não funciona porque ignora o ExecutionContext.
Se você usar o UnitaSk internamente, mas forneça o Valuetask como uma API externa, poderá escrevê -lo como o seguinte (inspirado no pooledawait).
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..
}
}
O .NET Core Version destina -se a permitir que os usuários usem o UnitaSk como uma interface ao compartilhar código com o Unity (como Cysharp/Magiconion). .NET A versão central do UnitaSk permite o compartilhamento de código suave.
Métodos de utilidade como quando são equivalentes ao UnitaSk são fornecidos como Cysharp/ValuetaskSuplemple.
Esta biblioteca está sob a licença do MIT.