Обеспечивает эффективную бесплатную асинхронную и ожидающую интеграцию для единства.
UniTask<T>
и пользовательский асинхетколдер для достижения нулевого распределенияUniTask.Yield
, UniTask.Delay
, UniTask.DelayFrame
и т. Д.), Которые включают замену всех операций Coroutine Технические детали см. В блоге пост: Unitask V2 - асинхронизация с нулевым распределением/ждать Unity, с асинхронным LINQ
Для продвинутых советов см. В блоге пост: расширяет UnityWebrequest через Async Decorator Pattern - расширенные методы Unitask
Установите через пакет UPM со ссылкой на git или пакетом активов ( UniTask.*.*.*.unitypackage
) доступны в Unitask/Выпуски.
// extension awaiter/methods can be used by this namespace
using Cysharp . Threading . Tasks ;
// You can return type as struct UniTask<T>(or UniTask), it is unity specialized lightweight alternative of Task<T>
// zero allocation and fast excution for zero overhead async/await integrate with Unity
async UniTask < string > DemoAsync ( )
{
// You can await Unity's AsyncObject
var asset = await Resources . LoadAsync < TextAsset > ( " foo " ) ;
var txt = ( await UnityWebRequest . Get ( " https://... " ) . SendWebRequest ( ) ) . downloadHandler . text ;
await SceneManager . LoadSceneAsync ( " scene2 " ) ;
// .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
// after Unity 2022.2, you can use `destroyCancellationToken` in MonoBehaviour
var asset2 = await Resources . LoadAsync < TextAsset > ( " bar " ) . WithCancellation ( this . GetCancellationTokenOnDestroy ( ) ) ;
// .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
var asset3 = await Resources . LoadAsync < TextAsset > ( " baz " ) . ToUniTask ( Progress . Create < float > ( x => Debug . Log ( x ) ) ) ;
// await frame-based operation like a coroutine
await UniTask . DelayFrame ( 100 ) ;
// replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
await UniTask . Delay ( TimeSpan . FromSeconds ( 10 ) , ignoreTimeScale : false ) ;
// yield any playerloop timing(PreUpdate, Update, LateUpdate, etc...)
await UniTask . Yield ( PlayerLoopTiming . PreLateUpdate ) ;
// replacement of yield return null
await UniTask . Yield ( ) ;
await UniTask . NextFrame ( ) ;
// replacement of WaitForEndOfFrame
# if UNITY_2023_1_OR_NEWER
await UniTask . WaitForEndOfFrame ( ) ;
# else
// requires MonoBehaviour(CoroutineRunner))
await UniTask . WaitForEndOfFrame ( this ) ; // this is MonoBehaviour
#endif
// replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
await UniTask . WaitForFixedUpdate ( ) ;
// replacement of yield return WaitUntil
await UniTask . WaitUntil ( ( ) => isActive == false ) ;
// special helper of WaitUntil
await UniTask . WaitUntilValueChanged ( this , x => x . isActive ) ;
// You can await IEnumerator coroutines
await FooCoroutineEnumerator ( ) ;
// You can await a standard task
await Task . Run ( ( ) => 100 ) ;
// Multithreading, run on ThreadPool under this code
await UniTask . SwitchToThreadPool ( ) ;
/* work on ThreadPool */
// return to MainThread(same as `ObserveOnMainThread` in UniRx)
await UniTask . SwitchToMainThread ( ) ;
// get async webrequest
async UniTask < string > GetTextAsync ( UnityWebRequest req )
{
var op = await req . SendWebRequest ( ) ;
return op . downloadHandler . text ;
}
var task1 = GetTextAsync ( UnityWebRequest . Get ( " http://google.com " ) ) ;
var task2 = GetTextAsync ( UnityWebRequest . Get ( " http://bing.com " ) ) ;
var task3 = GetTextAsync ( UnityWebRequest . Get ( " http://yahoo.com " ) ) ;
// concurrent async-wait and get results easily by tuple syntax
var ( google , bing , yahoo ) = await UniTask . WhenAll ( task1 , task2 , task3 ) ;
// shorthand of WhenAll, tuple can await directly
var ( google2 , bing2 , yahoo2 ) = await ( task1 , task2 , task3 ) ;
// return async-value.(or you can use `UniTask`(no result), `UniTaskVoid`(fire and forget)).
return ( asset as TextAsset ) ? . text ?? throw new InvalidOperationException ( " Asset not found " ) ;
}
Функции Unitask полагаются на C# 7.0 (задача пользовательская функция асинхрологического строителя), поэтому требуемая версия Unity будет после Unity 2018.3
, официальная самая низкая версия, поддерживаемая Unity 2018.4.13f1
.
Почему требуется Unitask (пользовательский объект, похожий на задание)? Потому что задача слишком тяжелая и не соответствует потоковым потокам Unity (однопоточная). Unitask не использует потоки и синхронизацию Context/executionContext, потому что асинхронный объект Unity автоматизируется автоматизирован слоем двигателя Unity. Он достигает быстрее и более низкого распределения и полностью интегрирован с единством.
Вы можете ожидать AsyncOperation
, ResourceRequest
, AssetBundleRequest
, AssetBundleCreateRequest
, UnityWebRequestAsyncOperation
, AsyncGPUReadbackRequest
, IEnumerator
и других при using Cysharp.Threading.Tasks;
Полем
Unitask предоставляет три схемы методов расширения.
* await asyncOperation ;
* . WithCancellation ( CancellationToken ) ;
* . ToUniTask ( IProgress , PlayerLoopTiming , CancellationToken ) ;
WithCancellation
является простая версия ToUniTask
, оба возврата UniTask
. Подробнее о отмене см. В разделе «Отмена и обработка исключений».
Примечание. Ожидание непосредственно возвращается от нативного времени PlayerLoop, но с канцеляцией и Tounitask возвращаются из указанного PlayerLooptiming. Подробности о времени см. В разделе PlayerLoop.
Примечание. AssetBundLEREQUEST имеет
asset
иallAssets
, по умолчанию ожидается возвратasset
. Если вы хотите получитьallAssets
, вы можете использовать методAwaitForAllAssets()
.
Тип UniTask
может использовать такие коммунальные услуги, как UniTask.WhenAll
, UniTask.WhenAny
, UniTask.WhenEach
. Они похожи на Task.WhenAll
/ Task.WhenAny
, но тип возврата более полезен. Они возвращают ценные кортежи, чтобы вы могли деконструировать каждый результат и передавать несколько типов.
public async UniTaskVoid LoadManyAsync ( )
{
// parallel load.
var ( a , b , c ) = await UniTask . WhenAll (
LoadAsSprite ( " foo " ) ,
LoadAsSprite ( " bar " ) ,
LoadAsSprite ( " baz " ) ) ;
}
async UniTask < Sprite > LoadAsSprite ( string path )
{
var resource = await Resources . LoadAsync < Sprite > ( path ) ;
return ( resource as Sprite ) ;
}
Если вы хотите преобразовать обратный вызов в Unitask, вы можете использовать UniTaskCompletionSource<T>
, который является легким изданием TaskCompletionSource<T>
.
public UniTask < int > WrapByUniTaskCompletionSource ( )
{
var utcs = new UniTaskCompletionSource < int > ( ) ;
// when complete, call utcs.TrySetResult();
// when failed, call utcs.TrySetException();
// when cancel, call utcs.TrySetCanceled();
return utcs . Task ; //return UniTask<int>
}
Вы можете преобразовать задание -> Unitask: AsUniTask
, UniTask
-> UniTask<AsyncUnit>
: AsAsyncUnitUniTask
, UniTask<T>
-> UniTask
: AsUniTask
. UniTask<T>
-> Стоимость преобразования UniTask
бесплатна.
Если вы хотите преобразовать Async в Coroutine, вы можете использовать .ToCoroutine()
, это полезно, если вы хотите разрешить только использовать систему Coroutine.
Unitask не может дождаться дважды. Это аналогичное ограничение для Valuetask/IvaluetaskSource, введенного в стандарте .NET 2.1.
Следующие операции никогда не должны выполняться в экземпляре ValueTask:
- В ожидании экземпляра несколько раз.
- Вызовет Astask несколько раз.
- Использование .Result или .getAwaiter (). GetResult (), когда операция еще не завершена, или использует их несколько раз.
- Использование более чем одной из этих методов для потребления экземпляра.
Если вы делаете что -то из вышеперечисленного, результаты не определены.
var task = UniTask . DelayFrame ( 10 ) ;
await task ;
await task ; // NG, throws Exception
Храните в поле класса, вы можете использовать UniTask.Lazy
, который поддерживает звонки несколько раз. .Preserve()
допускает несколько вызовов (внутренне кэшированные результаты). Это полезно, когда есть несколько вызовов в области функции.
Также UniTaskCompletionSource
может ждать несколько раз и ожидать от многих вызывающих абонентов.
Некоторые фабричные методы Unitask имеют CancellationToken cancellationToken = default
. Кроме того, некоторые асинхронные операции для Unity имеют WithCancellation(CancellationToken)
и ToUniTask(..., CancellationToken cancellation = default)
.
Вы можете передать CancellationToken
в параметр стандартной CancellationTokenSource
.
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 может быть создан путем CancellationTokenSource
или метода расширения Monobehaviour GetCancellationTokenOnDestroy
.
// this CancellationToken lifecycle is same as GameObject.
await UniTask . DelayFrame ( 1000 , cancellationToken : this . GetCancellationTokenOnDestroy ( ) ) ;
Для отмены распространения все асинхронные методы рекомендуют принять отмену CancellationToken cancellationToken
с последним аргументом и передать CancellationToken
от корня к концу.
await FooAsync ( this . GetCancellationTokenOnDestroy ( ) ) ;
// ---
async UniTask FooAsync ( CancellationToken cancellationToken )
{
await BarAsync ( cancellationToken ) ;
}
async UniTask BarAsync ( CancellationToken cancellationToken )
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) , cancellationToken ) ;
}
CancellationToken
означает жизненный цикл Async. Вместо этого вы можете удерживать свой собственный жизненный цикл от CancellationTokenondestroy.
public class MyBehaviour : MonoBehaviour
{
CancellationTokenSource disableCancellation = new CancellationTokenSource ( ) ;
CancellationTokenSource destroyCancellation = new CancellationTokenSource ( ) ;
private void OnEnable ( )
{
if ( disableCancellation != null )
{
disableCancellation . Dispose ( ) ;
}
disableCancellation = new CancellationTokenSource ( ) ;
}
private void OnDisable ( )
{
disableCancellation . Cancel ( ) ;
}
private void OnDestroy ( )
{
destroyCancellation . Cancel ( ) ;
destroyCancellation . Dispose ( ) ;
}
}
После Unity 2022.2 Unity добавляет отмену в Monobehaviour.destroycancellationtoken и Application.exitcancellationtoken.
При обнаружении отмены все методы бросают OperationCanceledException
и распространяются вверх по течению. Когда исключение (не ограничивается OperationCanceledException
) не обрабатывается в Async Mepale, он, наконец, распространяется в UniTaskScheduler.UnobservedTaskException
. Поведение по умолчанию полученного неразрывного исключения состоит в том, чтобы написать журнал как исключение. Уровень журнала может быть изменен с использованием UniTaskScheduler.UnobservedExceptionWriteLogType
. Если вы хотите использовать пользовательское поведение, установите действие на UniTaskScheduler.UnobservedTaskException.
А также OperationCanceledException
является особым исключением, это молча игнорируется при UnobservedTaskException
.
Если вы хотите отменить поведение в методе Async Unitask, бросьте OperationCanceledException
вручную.
public async UniTask < int > FooAsync ( )
{
await UniTask . Yield ( ) ;
throw new OperationCanceledException ( ) ;
}
Если вы обрабатываете исключение, но хотите игнорировать (распространять на глобальную обработку отмены), используйте фильтр исключений.
public async UniTask < int > BarAsync ( )
{
try
{
var x = await FooAsync ( ) ;
return x * 2 ;
}
catch ( Exception ex ) when ( ! ( ex is OperationCanceledException ) ) // when (ex is not OperationCanceledException) at C# 9.0
{
return - 1 ;
}
}
Выбросы/уловка OperationCanceledException
немного тяжелая, поэтому, если производительность вызывает беспокойство, используйте UniTask.SuppressCancellationThrow
, чтобы избежать операции. Он возвращается (bool IsCanceled, T Result)
вместо бросания.
var ( isCanceled , _ ) = await UniTask . DelayFrame ( 10 , cancellationToken : cts . Token ) . SuppressCancellationThrow ( ) ;
if ( isCanceled )
{
// ...
}
ПРИМЕЧАНИЕ. Подавите броски только в том случае, если вы вызовите непосредственно в наибольший метод источника. В противном случае возвратное значение будет преобразовано, но весь трубопровод не будет подавлять броски.
Некоторые функции, которые используют петлю игрока Unity, такие как UniTask.Yield
и UniTask.Delay
и т. Д., Определяет состояние CancellationToken на петле игрока. Это означает, что он не отменяется сразу после увольнения CancellationToken
.
Если вы хотите изменить это поведение, отмена немедленной, установите cancelImmediately
флага в качестве аргумента.
await UniTask . Yield ( cancellationToken , cancelImmediately : true ) ;
ПРИМЕЧАНИЕ. Настройка cancelImmediately
на истину и обнаружение немедленной отмены является более дорогостоящим, чем поведение по умолчанию. Это потому, что он использует CancellationToken.Register
; Он тяжелее, чем проверять отмену на петле игрока.
Тайм -аут - это вариация отмены. Вы можете установить тайм -аут с помощью CancellationTokenSouce.CancelAfterSlim(TimeSpan)
и передать отмену Async Methods.
var cts = new CancellationTokenSource ( ) ;
cts . CancelAfterSlim ( TimeSpan . FromSeconds ( 5 ) ) ; // 5sec timeout.
try
{
await UnityWebRequest . Get ( " http://foo " ) . SendWebRequest ( ) . WithCancellation ( cts . Token ) ;
}
catch ( OperationCanceledException ex )
{
if ( ex . CancellationToken == cts . Token )
{
UnityEngine . Debug . Log ( " Timeout " ) ;
}
}
CancellationTokenSouce.CancelAfter
- это стандартный API. Однако в Unity вы не должны использовать его, потому что это зависит от таймера потока.CancelAfterSlim
- это методы расширения Unitask, вместо этого он использует Playerloop.
Если вы хотите использовать тайм -аут с другим источником отмены, используйте CancellationTokenSource.CreateLinkedTokenSource
.
var cancelToken = new CancellationTokenSource ( ) ;
cancelButton . onClick . AddListener ( ( ) =>
{
cancelToken . Cancel ( ) ; // cancel from button click.
} ) ;
var timeoutToken = new CancellationTokenSource ( ) ;
timeoutToken . CancelAfterSlim ( TimeSpan . FromSeconds ( 5 ) ) ; // 5sec timeout.
try
{
// combine token
var linkedTokenSource = CancellationTokenSource . CreateLinkedTokenSource ( cancelToken . Token , timeoutToken . Token ) ;
await UnityWebRequest . Get ( " http://foo " ) . SendWebRequest ( ) . WithCancellation ( linkedTokenSource . Token ) ;
}
catch ( OperationCanceledException ex )
{
if ( timeoutToken . IsCancellationRequested )
{
UnityEngine . Debug . Log ( " Timeout. " ) ;
}
else if ( cancelToken . IsCancellationRequested )
{
UnityEngine . Debug . Log ( " Cancel clicked. " ) ;
}
}
Оптимизируйте для уменьшения распределения отмены TimeoutController
TimeoutController timeoutController = new TimeoutController ( ) ; // setup to field for reuse.
async UniTask FooAsync ( )
{
try
{
// you can pass timeoutController.Timeout(TimeSpan) to cancellationToken.
await UnityWebRequest . Get ( " http://foo " ) . SendWebRequest ( )
. WithCancellation ( timeoutController . Timeout ( TimeSpan . FromSeconds ( 5 ) ) ) ;
timeoutController . Reset ( ) ; // call Reset(Stop timeout timer and ready for reuse) when succeed.
}
catch ( OperationCanceledException ex )
{
if ( timeoutController . IsTimeout ( ) )
{
UnityEngine . Debug . Log ( " timeout " ) ;
}
}
}
Если вы хотите использовать тайм -аут с другим источником отмены, используйте new TimeoutController(CancellationToken)
.
TimeoutController timeoutController ;
CancellationTokenSource clickCancelSource ;
void Start ( )
{
this . clickCancelSource = new CancellationTokenSource ( ) ;
this . timeoutController = new TimeoutController ( clickCancelSource ) ;
}
Примечание. У Unitask есть .Timeout
,. .TimeoutWithoutException
Методы, если это возможно, не используйте их, пожалуйста, передайте CancellationToken
. Потому что. .Timeout
работает от внешней задачи, не может остановить тайм -полученную задачу. .Timeout
означает игнорировать результат, когда тайм -аут. Если вы передадите CancellationToken
метода, он будет действовать изнутри задачи, поэтому можно остановить выполнение задачи.
В некоторых асинхровых операциях для Unity есть методы расширения ToUniTask(IProgress<float> progress = null, ...)
.
var progress = Progress . Create < float > ( x => Debug . Log ( x ) ) ;
var request = await UnityWebRequest . Get ( " http://google.co.jp " )
. SendWebRequest ( )
. ToUniTask ( progress : progress ) ;
Вы не должны использовать стандартную new System.Progress<T>
, потому что она вызывает распределение каждый раз. Используйте Cysharp.Threading.Tasks.Progress
вместо этого. Эта фабрика прогресса имеет два метода, Create
и CreateOnlyValueChanged
. CreateOnlyValueChanged
вызовы только тогда, когда значение прогресса изменилось.
Реализация интерфейса iProgress для вызывающего абонента лучше, так как нет распределения Lambda.
public class Foo : MonoBehaviour , IProgress < float >
{
public void Report ( float value )
{
UnityEngine . Debug . Log ( value ) ;
}
public async UniTaskVoid WebRequest ( )
{
var request = await UnityWebRequest . Get ( " http://google.co.jp " )
. SendWebRequest ( )
. ToUniTask ( progress : this ) ; // pass this
}
}
Unitask запускается на пользовательском PlayerLoop. Методы на основе PlayerLoop от Unitask PlayerLoopTiming
такие как Delay
, DelayFrame
, asyncOperation.ToUniTask
.
public enum PlayerLoopTiming
{
Initialization = 0 ,
LastInitialization = 1 ,
EarlyUpdate = 2 ,
LastEarlyUpdate = 3 ,
FixedUpdate = 4 ,
LastFixedUpdate = 5 ,
PreUpdate = 6 ,
LastPreUpdate = 7 ,
Update = 8 ,
LastUpdate = 9 ,
PreLateUpdate = 10 ,
LastPreLateUpdate = 11 ,
PostLateUpdate = 12 ,
LastPostLateUpdate = 13
# if UNITY_2020_2_OR_NEWER
TimeUpdate = 14 ,
LastTimeUpdate = 15 ,
#endif
}
Это указывает, когда для запуска вы можете проверить Playerlooplist.md на Unity по умолчанию PlayerLoop и инъекционную контуку Unitask.
PlayerLoopTiming.Update
аналогичен yield return null
в Coroutine, но он вызывается до обновления (Update и Ugui Events (Button.onclick и т. Д.) Вызывается на ScriptRunBehaviourUpdate
, дайте возврат нуля на ScriptRunDelayedDynamicFrameRate
). PlayerLoopTiming.FixedUpdate
похож на WaitForFixedUpdate
.
PlayerLoopTiming.LastPostLateUpdate
не эквивалентенyield return new WaitForEndOfFrame()
. Waitforendofframe от Coroutine, похоже, работает после того, как PlayerLoop закончится. Некоторые методы, которые требуют конца кадры Coroutine (Texture2D.ReadPixels
,ScreenCapture.CaptureScreenshotAsTexture
,CommandBuffer
и т. Д.) Не работают правильно при замене асинхронным/ожиданием. В этих случаях проходит Monobehaviour (Coroutine Runnner) вUniTask.WaitForEndOfFrame
. Например,await UniTask.WaitForEndOfFrame(this);
Легкая альтернатива доходности - это легкая альтернативаyield return new WaitForEndOfFrame()
.Примечание. В Unity 2023.1 или более нове,
await UniTask.WaitForEndOfFrame();
Больше не требует монобингавиура. Он используетUnityEngine.Awaitable.EndOfFrameAsync
.
yield return null
и UniTask.Yield
похожи, но разные. yield return null
всегда возвращает следующий кадр, но UniTask.Yield
возвращается в следующий раз. То есть, вызовов UniTask.Yield(PlayerLoopTiming.Update)
На PreUpdate
, он возвращает тот же кадр. UniTask.NextFrame()
гарантирует возврат следующего кадра, вы можете ожидать, что это будет вести себя точно так же, как и yield return null
.
Unitask.yield (без отмены TOWER) - это специальный тип, возвращает
YieldAwaitable
и работает на урожайности. Это самый легкий и быстрый.
AsyncOperation
возвращается из местного времени. Например, watist SceneManager.LoadSceneAsync
возвращается из EarlyUpdate.UpdatePreloading
, и после того, как его называют, Start
загруженной сцены вызывается от EarlyUpdate.ScriptRunDelayedStartupFrame
. Также await UnityWebRequest
возвращается из EarlyUpdate.ExecuteMainThreadJobs
.
В Unitask ожидание непосредственно использует нативное время, в то время как WithCancellation
и ToUniTask
используют указанное время. Обычно это не особая проблема, но с LoadSceneAsync
это вызывает другой порядок начала и продолжения после ожидания. Таким образом, рекомендуется не использовать LoadSceneAsync.ToUniTask
.
Примечание. При использовании Unity 2023.1 или новее убедитесь, что у вас есть
using UnityEngine;
в использовании операторов вашего файла при работе с NewUnityEngine.Awaitable
SceneManager.LoadSceneAsync
Это предотвращает ошибки компиляции, избегая использования версииUnityEngine.AsyncOperation
.
В Stacktrace вы можете проверить, где он работает в Playerloop.
По умолчанию Unitask PlayerLoop инициализируется в [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
.
Порядок, в котором методы называются в BeforescenEload, является неэнергиничным, поэтому, если вы хотите использовать Unitask в других методах BeforescenEload, вы должны попытаться инициализировать его до этого.
// AfterAssembliesLoaded is called before BeforeSceneLoad
[ RuntimeInitializeOnLoadMethod ( RuntimeInitializeLoadType . AfterAssembliesLoaded ) ]
public static void InitUniTaskLoop ( )
{
var loop = PlayerLoop . GetCurrentPlayerLoop ( ) ;
Cysharp . Threading . Tasks . PlayerLoopHelper . Initialize ( ref loop ) ;
}
Если вы импортируете пакет Entities
Unity, он сбрасывает пользовательский петлю игрока в соответствии с по умолчанию в BeforeSceneLoad
и вводит цикл ECS. Когда Unity вызывает метод инъекции ECS после метода инициализации Unitask, Unitask больше не будет работать.
Чтобы решить эту проблему, вы можете повторно инициализировать Unitask PlayerLoop после инициализации ECS.
// Get ECS Loop.
var playerLoop = ScriptBehaviourUpdateOrder . CurrentPlayerLoop ;
// Setup UniTask's PlayerLoop.
PlayerLoopHelper . Initialize ( ref playerLoop ) ;
Вы можете диагностировать, готов ли петля Unitask Player, позвонив PlayerLoopHelper.IsInjectedUniTaskPlayerLoop()
. А также PlayerLoopHelper.DumpCurrentPlayerLoop
регистрирует все текущие игроки в консоли.
void Start ( )
{
UnityEngine . Debug . Log ( " UniTaskPlayerLoop ready? " + PlayerLoopHelper . IsInjectedUniTaskPlayerLoop ( ) ) ;
PlayerLoopHelper . DumpCurrentPlayerLoop ( ) ;
}
Вы можете немного оптимизировать стоимость петли, удалив UNISE PlayerLooptiming впрыска. Вы можете назвать PlayerLoopHelper.Initialize(InjectPlayerLoopTimings)
на инициализация.
var loop = PlayerLoop . GetCurrentPlayerLoop ( ) ;
PlayerLoopHelper . Initialize ( ref loop , InjectPlayerLoopTimings . Minimum ) ; // minimum is Update | FixedUpdate | LastPostLateUpdate
InjectPlayerLoopTimings
имеет три предустановленные, All
и Standard
(все без последнего, кроме LastPostlateUpdate), Minimum
( Update | FixedUpdate | LastPostLateUpdate
). По умолчанию все, и вы можете объединить пользовательские временные районы, такие как InjectPlayerLoopTimings.Update | InjectPlayerLoopTimings.FixedUpdate | InjectPlayerLoopTimings.PreLateUpdate
.
Вы можете допустить ошибку, чтобы использовать неинъектированные PlayerLoopTiming
с помощью Microsoft.codeanalysis.bannaapianalyzers. Например, вы можете настроить BannedSymbols.txt
, как это для InjectPlayerLoopTimings.Minimum
.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.Initialization; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastInitialization; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.EarlyUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastEarlyUpdate; Isn't injected this PlayerLoop in this project.d
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastFixedUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.PreUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastPreUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.PreLateUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastPreLateUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.PostLateUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.TimeUpdate; Isn't injected this PlayerLoop in this project.
F:Cysharp.Threading.Tasks.PlayerLoopTiming.LastTimeUpdate; Isn't injected this PlayerLoop in this project.
Вы можете настроить тяжесть RS0030
на ошибку.
async void
- это стандартная система задач C, поэтому она не работает в системах Unitask. Лучше не использовать его. async UniTaskVoid
- это легкая версия async UniTask
, потому что у него нет ожидаемого завершения, и немедленно сообщает об ошибках UniTaskScheduler.UnobservedTaskException
. Если вам не требуется ожидание (огонь и забыть), использование UniTaskVoid
лучше. К сожалению, чтобы отклонить предупреждение, вам нужно позвонить Forget()
.
public async UniTaskVoid FireAndForgetMethod ( )
{
// do anything...
await UniTask . Yield ( ) ;
}
public void Caller ( )
{
FireAndForgetMethod ( ) . Forget ( ) ;
}
Также Unitask имеет метод Forget
, он похож на UniTaskVoid
и имеет те же эффекты. Однако UniTaskVoid
более эффективен, если вы полностью не используете await
。
public async UniTask DoAsync ( )
{
// do anything...
await UniTask . Yield ( ) ;
}
public void Caller ( )
{
DoAsync ( ) . Forget ( ) ;
}
Чтобы использовать Async Lambda, зарегистрированную на мероприятии, не используйте async void
. Вместо этого вы можете использовать UniTask.Action
или UniTask.UnityAction
, оба из которых создают делегат через async UniTaskVoid
Lambda.
Action actEvent ;
UnityAction unityEvent ; // especially used in uGUI
// Bad: async void
actEvent += async ( ) => { } ;
unityEvent += async ( ) => { } ;
// Ok: create Action delegate by lambda
actEvent += UniTask . Action ( async ( ) => { await UniTask . Yield ( ) ; } ) ;
unityEvent += UniTask . UnityAction ( async ( ) => { await UniTask . Yield ( ) ; } ) ;
UniTaskVoid
также может использоваться в методе Start
Monobehaviour.
class Sample : MonoBehaviour
{
async UniTaskVoid Start ( )
{
// async init code.
}
}
Полезно для проверки (просочившихся) унитатов. Вы можете открыть окно трекера в Window -> UniTask Tracker
.
UnitaskTracker предназначена для отладки, используя только так, чтобы включение отслеживания и захвата StackTraces полезно, но оказывает высокое влияние на производительность. Рекомендуемое использование заключается в том, чтобы включить как отслеживание, так и стектрас, чтобы найти утечки задач и отключить их обоих, когда это сделано.
По умолчанию Unitask поддерживает расширения событий TextMeshPRO ( BindTo(TMP_Text)
и TMP_InputField
такие как Standard Ugui InputField
), Dotween ( Tween
как ожидаемый) и адресаты ( AsyncOperationHandle
и AsyncOperationHandle<T>
как ожидается).
Есть определено в разделенных asmdefs, таких как UniTask.TextMeshPro
, UniTask.DOTween
, UniTask.Addressables
.
Поддержка TextMeshPro и Addersables автоматически включена при импорте своих пакетов из диспетчера пакетов. Однако для поддержки между точки зрения, после импорта из точечных активов и определения сценария определить символ UNITASK_DOTWEEN_SUPPORT
чтобы включить его.
// sequential
await transform . DOMoveX ( 2 , 10 ) ;
await transform . DOMoveZ ( 5 , 20 ) ;
// parallel with cancellation
var ct = this . GetCancellationTokenOnDestroy ( ) ;
await UniTask . WhenAll (
transform . DOMoveX ( 10 , 3 ) . WithCancellation ( ct ) ,
transform . DOScale ( 10 , 3 ) . WithCancellation ( ct ) ) ;
Поведение по умолчанию ( await
, WithCancellation
, ToUniTask
) ожидает, что Tween убит. Он работает как на полном (истинном/ложном), так и на убийстве (true/false). Но если вы хотите повторно использовать Tweens ( SetAutoKill(false)
), это не работает, как ожидалось. Если вы хотите ждать другого времени, в Tween, AwaitForComplete
, AwaitForPause
, AwaitForPlay
, AwaitForRewind
, AwaitForComplete, существуют следующие методы расширения, AwaitForStepComplete
.
Unity 2020.2 поддерживает C# 8.0, поэтому вы можете использовать await foreach
. Это новое обновление нотации в асинхронную эру.
// Unity 2020.2, C# 8.0
await foreach ( var _ in UniTaskAsyncEnumerable . EveryUpdate ( ) . WithCancellation ( token ) )
{
Debug . Log ( " Update() " + Time . frameCount ) ;
}
В среде C# 7.3 вы можете использовать метод ForEachAsync
для работы практически таким же образом.
// C# 7.3(Unity 2018.3~)
await UniTaskAsyncEnumerable . EveryUpdate ( ) . ForEachAsync ( _ =>
{
Debug . Log ( " Update() " + Time . frameCount ) ;
} , token ) ;
UniTask.WhenEach
, это похоже на Task.WhenEach
.NET 9.Wheeach может потреблять новый путь для ожидания нескольких задач.
await foreach ( var result in UniTask . WhenEach ( task1 , task2 , task3 ) )
{
// The result is of type WhenEachResult<T>.
// It contains either `T Result` or `Exception Exception`.
// You can check `IsCompletedSuccessfully` or `IsFaulted` to determine whether to access `.Result` or `.Exception`.
// If you want to throw an exception when `IsFaulted` and retrieve the result when successful, use `GetResult()`.
Debug . Log ( result . GetResult ( ) ) ;
}
UnitaskAsyncenumerable реализует асинхронный LINQ, похожий на LINQ в IEnumerable<T>
или Rx в IObservable<T>
. Все стандартные операторы запросов LINQ могут быть применены к асинхронным потокам. Например, в следующем коде показано, как применить фильтр к кнопке кнопку асинхронного потока, который запускается один раз каждые два нажатия.
await okButton . OnClickAsAsyncEnumerable ( ) . Where ( ( x , i ) => i % 2 == 0 ) . ForEachAsync ( _ =>
{
} ) ;
Стиль огня и забывания (например, обработка событий), вы также можете использовать Subscribe
.
okButton . OnClickAsAsyncEnumerable ( ) . Where ( ( x , i ) => i % 2 == 0 ) . Subscribe ( _ =>
{
} ) ;
Async LINQ включен при using Cysharp.Threading.Tasks.Linq;
и UniTaskAsyncEnumerable
определяется в UniTask.Linq
asmdef.
Это ближе к Unirx (реактивные расширения), но Unitaskasyncenumerable-это асинхронный поток на основе притяжения, тогда как Rx был асинхронным потоком на основе Push. Обратите внимание, что, хотя и похожи, характеристики различны, и детали ведут себя по -разному вместе с ними.
UniTaskAsyncEnumerable
- это точка входа, как Enumerable
. В дополнение к стандартным операторам запросов, существуют другие генераторы для единства, таких как EveryUpdate
, Timer
, TimerFrame
, Interval
, IntervalFrame
и EveryValueChanged
. And also added additional UniTask original query operators like Append
, Prepend
, DistinctUntilChanged
, ToHashSet
, Buffer
, CombineLatest
, Merge
Do
, Never
, ForEachAsync
, Pairwise
, Publish
, Queue
, Return
, SkipUntil
, TakeUntil
, SkipUntilCanceled
, TakeUntilCanceled
, TakeLast
, Subscribe
.
Метод с фанком в качестве аргумента имеет три дополнительных перегрузки, ***Await
, ***AwaitWithCancellation
.
Select ( Func < T , TR > selector )
SelectAwait ( Func < T , UniTask < TR > > selector )
SelectAwaitWithCancellation ( Func < T , CancellationToken , UniTask < TR > > selector )
Если вы хотите использовать метод async
внутри Func, используйте ***Await
или ***AwaitWithCancellation
.
Как создать асинхровый итератор: C# 8.0 поддерживает асинхронный итератор ( async yield return
), но он только позволяет только IAsyncEnumerable<T>
и, конечно, требует C# 8.0. Unitask поддерживает UniTaskAsyncEnumerable.Create
Метод создания пользовательского асинхронного итератора.
// IAsyncEnumerable, C# 8.0 version of async iterator. ( do not use this style, IAsyncEnumerable is not controled in UniTask).
public async IAsyncEnumerable < int > MyEveryUpdate ( [ EnumeratorCancellation ] CancellationToken cancelationToken = default )
{
var frameCount = 0 ;
await UniTask . Yield ( ) ;
while ( ! token . IsCancellationRequested )
{
yield return frameCount ++ ;
await UniTask . Yield ( ) ;
}
}
// UniTaskAsyncEnumerable.Create and use `await writer.YieldAsync` instead of `yield return`.
public IUniTaskAsyncEnumerable < int > MyEveryUpdate ( )
{
// writer(IAsyncWriter<T>) has `YieldAsync(value)` method.
return UniTaskAsyncEnumerable . Create < int > ( async ( writer , token ) =>
{
var frameCount = 0 ;
await UniTask . Yield ( ) ;
while ( ! token . IsCancellationRequested )
{
await writer . YieldAsync ( frameCount ++ ) ; // instead of `yield return`
await UniTask . Yield ( ) ;
}
} ) ;
}
Все компоненты Ugui реализуют ***AsAsyncEnumerable
, чтобы преобразовать асинхронные потоки событий.
async UniTask TripleClick ( )
{
// In default, used button.GetCancellationTokenOnDestroy to manage lieftime of async
await button . OnClickAsync ( ) ;
await button . OnClickAsync ( ) ;
await button . OnClickAsync ( ) ;
Debug . Log ( " Three times clicked " ) ;
}
// more efficient way
async UniTask TripleClick ( )
{
using ( var handler = button . GetAsyncClickEventHandler ( ) )
{
await handler . OnClickAsync ( ) ;
await handler . OnClickAsync ( ) ;
await handler . OnClickAsync ( ) ;
Debug . Log ( " Three times clicked " ) ;
}
}
// use async LINQ
async UniTask TripleClick ( CancellationToken token )
{
await button . OnClickAsAsyncEnumerable ( ) . Take ( 3 ) . Last ( ) ;
Debug . Log ( " Three times clicked " ) ;
}
// use async LINQ2
async UniTask TripleClick ( CancellationToken token )
{
await button . OnClickAsAsyncEnumerable ( ) . Take ( 3 ) . ForEachAsync ( _ =>
{
Debug . Log ( " Every clicked " ) ;
} ) ;
Debug . Log ( " Three times clicked, complete. " ) ;
}
Все события сообщений Monobehaviour могут преобразовать асинхронные потоки AsyncTriggers
, которые можно включить с using Cysharp.Threading.Tasks.Triggers;
Полем Asynctrigger может быть создан с использованием GetAsync***Trigger
, и запускает себя как UnitAskAsyncEnumerable.
var trigger = this . GetOnCollisionEnterAsyncHandler ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
await trigger . OnCollisionEnterAsync ( ) ;
// every moves.
await this . GetAsyncMoveTrigger ( ) . ForEachAsync ( axisEventData =>
{
} ) ;
AsyncReactiveProperty
, AsyncReadOnlyReactiveProperty
- это версия ReactiveProperty Unitask. Метод расширения BindTo
IUniTaskAsyncEnumerable<T>
для связывания асинхронных значений потока с компонентами 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 ) ;
Асинхронный поток типа тяги не получает следующие значения, пока асинхронная обработка в последовательности не будет завершена. Это может различить данные из событий типа нажимания, таких как кнопки.
// can not get click event during 3 seconds complete.
await button . OnClickAsAsyncEnumerable ( ) . ForEachAwaitAsync ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
Это полезно (предотвратить дважды щелкнуть), но иногда не полезно.
Использование метода Queue()
также будет стоять в очереди события во время асинхронной обработки.
// queued message in asynchronous processing
await button . OnClickAsAsyncEnumerable ( ) . Queue ( ) . ForEachAwaitAsync ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
Или используйте Subscribe
, огонь и забывайте стиль.
button . OnClickAsAsyncEnumerable ( ) . Subscribe ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
Channel
такой же, как и System.Threading.Tasks.Channels, что похоже на канал Голанга.
В настоящее время он поддерживает только несколько продюсеров, однопотребляющих неограниченных каналов. Он может создавать по Channel.CreateSingleConsumerUnbounded<T>()
.
Для продюсера ( .Writer
) используйте TryWrite
, чтобы увеличить значение и TryComplete
для завершения канала. Для потребителя ( .Reader
) используйте TryRead
, WaitToReadAsync
, ReadAsync
, Completion
и ReadAllAsync
для чтения очередных сообщений.
ReadAllAsync
возвращает IUniTaskAsyncEnumerable<T>
так же запросить операторы LINQ. Читатель разрешает только однопотребительский, но использует оператор запроса .Publish()
для включения многоадресного сообщения. Например, создайте паб/субъекту.
public class AsyncMessageBroker < T > : IDisposable
{
Channel < T > channel ;
IConnectableUniTaskAsyncEnumerable < T > multicastSource ;
IDisposable connection ;
public AsyncMessageBroker ( )
{
channel = Channel . CreateSingleConsumerUnbounded < T > ( ) ;
multicastSource = channel . Reader . ReadAllAsync ( ) . Publish ( ) ;
connection = multicastSource . Connect ( ) ; // Publish returns IConnectableUniTaskAsyncEnumerable.
}
public void Publish ( T value )
{
channel . Writer . TryWrite ( value ) ;
}
public IUniTaskAsyncEnumerable < T > Subscribe ( )
{
return multicastSource ;
}
public void Dispose ( )
{
channel . Writer . TryComplete ( ) ;
connection . Dispose ( ) ;
}
}
Unity 6 представляет ожидаемый тип, ожидаемый. Проще говоря, ожидаемые можно считать подмножеством единиц, и на самом деле, на дизайн ожидания повлиял Unitask. Он должен быть в состоянии обработать на основе PlayerLoop ожидания, объединенные задачи и поддержку отмены с помощью CancellationToken
аналогичным образом. С включением в стандартную библиотеку, вы можете задаться вопросом, следует ли продолжать использовать Unitask или мигрировать, чтобы ожидать. Вот краткое руководство.
Во -первых, функциональность, предоставленная ожидаемой, эквивалентна тому, что предлагает Coroutines. Вместо yield return
вы используете ожидание; await NextFrameAsync()
заменяет yield return null
; И есть эквиваленты для WaitForSeconds
и EndOfFrame
. Однако это такая степень. Будучи основанным на корысии с точки зрения функциональности, ему не хватает функций, основанных на задачах. В практической разработке приложений с использованием Async/watiate, такие операции, как WhenAll
, необходимы. Кроме того, Unitask обеспечивает множество операций на основе кадров (таких как DelayFrame
) и более гибкий контроль PlayerLooptiming, которые недоступны в ожидании. Конечно, нет окна трекера.
Поэтому я рекомендую использовать Unitask для разработки приложений. Unitask - это суперсет ожидаемого и включает в себя много важных функций. Для разработки библиотеки, где вы хотите избежать внешних зависимостей, было бы уместно использование ожидаемого в качестве возврата для методов. Ожидание можно преобразовать в Unitask с использованием AsUniTask
, поэтому нет проблем в обработке функциональности на основе ожидаемых в библиотеке Unitask. Конечно, если вам не нужно беспокоиться о зависимостях, использование Unitask было бы лучшим выбором даже для разработки библиотеки.
Атрибут Unity [UnityTest]
может проверить Coroutine (ienumerator), но не может проверить асинхронность. UniTask.ToCoroutine
Bridges async/ждать Coroutine, чтобы вы могли проверить асинхронные методы.
[ UnityTest ]
public IEnumerator DelayIgnore ( ) => UniTask . ToCoroutine ( async ( ) =>
{
var time = Time . realtimeSinceStartup ;
Time . timeScale = 0.5f ;
try
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) , ignoreTimeScale : true ) ;
var elapsed = Time . realtimeSinceStartup - time ;
Assert . AreEqual ( 3 , ( int ) Math . Round ( TimeSpan . FromSeconds ( elapsed ) . TotalSeconds , MidpointRounding . ToEven ) ) ;
}
finally
{
Time . timeScale = 1.0f ;
}
} ) ;
Собственные модульные тесты Unitask записаны с использованием Test Runner Unity и Cysharp/RuntimeUnittestToolkit для интеграции с CI и проверить, работает ли IL2CPP.
Большинство методов Unitask работают на одном потоке (Playerloop), только с UniTask.Run
( Task.Run
Equivalent) и UniTask.SwitchToThreadPool
работающим в пуле потоков. Если вы используете пул потоков, он не будет работать с WebGL и так далее.
UniTask.Run
теперь устарел. Вместо этого вы можете использовать UniTask.RunOnThreadPool
. А также подумайте, можете ли вы использовать UniTask.Create
или UniTask.Void
.
Вы можете преобразовать Coroutine (ienumerator) в Unitask (или напрямую ждать), но у него есть некоторые ограничения.
WaitForEndOfFrame
/ WaitForFixedUpdate
/ Coroutine
не поддерживается.StartCoroutine
, он использует указанный PlayerLoopTiming
, а PlayerLoopTiming.Update
Update запускается до Update
Monobehaviour и StartCoroutine
. Если вы хотите полностью совместимое преобразование из Coroutine в Async, используйте перегрузку IEnumerator.ToUniTask(MonoBehaviour coroutineRunner)
. Он выполняет startCoroutine в экземпляре аргумента Monobehaviour и ожидает его завершения в Unitask.
Unitask может работать в редакторе Unity, как редактор Coroutine. Однако есть некоторые ограничения.
DelayType.Realtime
, который ожидает подходящего времени.EditorApplication.update
.-batchmode
с -quit
не работает, потому что Unity не запускает EditorApplication.update
и не бросается после одного кадра. Вместо этого не используйте -quit
и не бросайте вручную с EditorApplication.Exit(0)
. Unitask имеет много стандартных API, подобных задачам. Эта таблица показывает, что такое альтернативные API.
Используйте стандартный тип.
.NET Тип | Тип Unitask |
---|---|
IProgress<T> | --- |
CancellationToken | --- |
CancellationTokenSource | --- |
Используйте тип Unitask.
.NET Тип | Тип Unitask |
---|---|
Task / ValueTask | UniTask |
Task<T> / ValueTask<T> | UniTask<T> |
async void | async UniTaskVoid |
+= async () => { } | UniTask.Void , UniTask.Action , UniTask.UnityAction |
--- | UniTaskCompletionSource |
TaskCompletionSource<T> | UniTaskCompletionSource<T> / AutoResetUniTaskCompletionSource<T> |
ManualResetValueTaskSourceCore<T> | UniTaskCompletionSourceCore<T> |
IValueTaskSource | IUniTaskSource |
IValueTaskSource<T> | IUniTaskSource<T> |
ValueTask.IsCompleted | UniTask.Status.IsCompleted() |
ValueTask<T>.IsCompleted | UniTask<T>.Status.IsCompleted() |
new Progress<T> | Progress.Create<T> |
CancellationToken.Register(UnsafeRegister) | CancellationToken.RegisterWithoutCaptureExecutionContext |
CancellationTokenSource.CancelAfter | CancellationTokenSource.CancelAfterSlim |
Channel.CreateUnbounded<T>(false){ SingleReader = true } | Channel.CreateSingleConsumerUnbounded<T> |
IAsyncEnumerable<T> | IUniTaskAsyncEnumerable<T> |
IAsyncEnumerator<T> | IUniTaskAsyncEnumerator<T> |
IAsyncDisposable | IUniTaskAsyncDisposable |
Task.Delay | UniTask.Delay |
Task.Yield | UniTask.Yield |
Task.Run | UniTask.RunOnThreadPool |
Task.WhenAll | UniTask.WhenAll |
Task.WhenAny | UniTask.WhenAny |
Task.WhenEach | UniTask.WhenEach |
Task.CompletedTask | UniTask.CompletedTask |
Task.FromException | UniTask.FromException |
Task.FromResult | UniTask.FromResult |
Task.FromCanceled | UniTask.FromCanceled |
Task.ContinueWith | UniTask.ContinueWith |
TaskScheduler.UnobservedTaskException | UniTaskScheduler.UnobservedTaskException |
Unitask Agversysing Cachs Async обещают объекты для достижения нулевого распределения (для технических деталей см. В блоге Unitask v2 - асинхронизация с нулевым распределением/ждать Unity, с асинхронным LINQ). По умолчанию он кэширует все обещания, но вы можете настроить TaskPool.SetMaxPoolSize
на ваше значение, значение указывает размер кэша на тип. TaskPool.GetCacheSizeInfo
Возвращает в настоящее время кэшированные объекты в пуле.
foreach ( var ( type , size ) in TaskPool . GetCacheSizeInfo ( ) )
{
Debug . Log ( type + " : " + size ) ;
}
В UnityEditor профилировщик показывает распределение компилятора, сгенерированного асинкстатемахином, но это происходит только в сборке отладки (разработка). C# Компилятор генерирует AsyncStateMachine в качестве класса по сборке отладки и как struct при сборке выпуска.
Unity поддерживает опцию оптимизации кода, начиная с 2020.1 (справа, нижний колонтитул).
Вы можете изменить оптимизацию компилятора C#, чтобы освободить, чтобы удалить асинкстатемахин в сборке разработки. Эта опция оптимизации также может Compilation.CodeOptimization
установлена с помощью Compilation.CompilationPipeline-codeOptimization
.
Unity's SynchronizationContext ( UnitySynchronizationContext
) - плохая реализация для производительности. Unitask обходит SynchronizationContext
(и ExecutionContext
), поэтому он не использует его, но если существует в async Task
, все еще используется его. UniTaskSynchronizationContext
- это замена UnitySynchronizationContext
, который лучше для производительности.
public class SyncContextInjecter
{
[ RuntimeInitializeOnLoadMethod ( RuntimeInitializeLoadType . SubsystemRegistration ) ]
public static void Inject ( )
{
SynchronizationContext . SetSynchronizationContext ( new UniTaskSynchronizationContext ( ) ) ;
}
}
Это необязательный выбор и не всегда рекомендуется; UniTaskSynchronizationContext
менее производительна, чем async UniTask
, и не является полной заменой Unitask. Это также не гарантирует полную поведенческую совместимость с UnitySynchronizationContext
.
Ссылки на API Unitask размещаются по адресу cysharp.github.io/unitask от docfx и cysharp/docfxtemplate.
Например, заводские методы Unitask можно увидеть в методах Unitask#. Методы завода/расширения UnitaskAsyncenumerable можно увидеть на Unitaskasyncenmerable#Методы.
Требуется версия единства, которая поддерживает параметр запроса пути для пакетов GIT (Unity> = 2019.3.4f1, Unity> = 2020.1a21). Вы можете добавить https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
к менеджеру пакетов
или добавить "com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"
to Packages/manifest.json
.
Если вы хотите установить целевую версию, Unitask использует тег выпуска *.*.*
, Чтобы вы могли указать версию, такую как #2.1.0
. Например, https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0
.
Для .net Core используйте Nuget.
PM> Unit-Package Unitask
Unitask of Core версии .NET - это подмножество Unity Unitask с удаленными методами, зависимыми от PlayerLoop.
Он работает при более высокой производительности, чем стандартная задача/valuetask, но вы должны быть осторожны, чтобы игнорировать выполнение Conturetext/Synchronization Context при его использовании. AsyncLocal
также не работает, потому что он игнорирует execution context.
Если вы используете Unitask внутри, но предоставите ValueTask в качестве внешнего API, вы можете написать его как следующее (вдохновленное 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..
}
}
Основная версия .net предназначена для того, чтобы пользователи использовали Unitask в качестве интерфейса при обмене кодом с Unity (например, Cysharp/Magiconion). .NET CORE Version of Unitask позволяет обмен плавным кодом.
Методы полезности, такие как Whenall, которые эквивалентны Unitask, предоставляются в виде Cysharp/valuetasksuplement.
Эта библиотека находится под лицензией MIT.