يوفر تخصيصًا فعالًا لتكامل Async/انتظار الوحدة.
UniTask<T>
و AsyncMethodBuilder المخصصة لتحقيق تخصيص صفريةUniTask.Yield
، UniTask.Delay
، UniTask.DelayFrame
، إلخ.) التي تتيح استبدال جميع عمليات coroutine للحصول على التفاصيل الفنية ، راجع منشور المدونة: Unitask v2 - Zero Tinocation Async/في انتظار الوحدة ، مع LINK غير المتزامن
للحصول على نصائح متقدمة ، راجع منشور المدونة: يمتد UnityWebRequest عبر نمط Decorator Async - التقنيات المتقدمة لـ Unitask
قم بالتثبيت عبر حزمة UPM مع GIT Reference أو Package Asset ( UniTask.*.*.*.unitypackage
// 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 (ميزة Builder Custom Async الشبيهة بالمهمة) ، لذا فإن إصدار الوحدة المطلوب هو بعد Unity 2018.3
، وهو أقل إصدار رسمي مدعوم هو Unity 2018.4.13f1
.
لماذا يطلب Unitask (كائن مخصص يشبه المهمة)؟ لأن المهمة ثقيلة للغاية ولا تتطابق مع خيوط الوحدة (الخيط الفردي). لا تستخدم Unitask مؤشرات الترابط و SynchronizationContext/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 ولكن يتم إرجاع withcancellation و tounitask من اللاعب المحدد. للحصول على تفاصيل التوقيت ، راجع: قسم PlayerLoop.
ملاحظة: AssetBundLereQuest لديه
asset
allAssets
، وانتظار الافتراضيasset
. إذا كنت ترغب في الحصول علىallAssets
، فيمكنك استخدام طريقةAwaitForAllAssets()
.
يمكن لنوع UniTask
استخدام المرافق مثل UniTask.WhenAll
، UniTask.WhenAny
، UniTask.WhenEach
. هم Task.WhenAny
Task.WhenAll
. يقومون بإرجاع tuples القيمة حتى تتمكن من تفكيك كل نتيجة وتمرير أنواع متعددة.
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 Standard 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
. أيضا بعض العمليات غير المتزامنة للوحدة لها 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 ( ) ) ;
لإلغاء الانتشار ، توصي جميع طريقة ASYNC بقبول 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. يمكنك الاحتفاظ بدورة حياتك الخاصة بدلاً من الإلغاء الافتراضي.
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 ( ) ;
}
}
بعد الوحدة 2022.2 ، تضيف Unity cancellationToken في monobehaviour.destroycancellationToken و Application.exitCancellationToken.
عند الكشف عن الإلغاء ، ترمي جميع الطرق OperationCanceledException
وانتشر المنبع. عندما لا يتم التعامل مع الاستثناء (لا يقتصر على OperationCanceledException
) في طريقة Async ، يتم نشره أخيرًا إلى UniTaskScheduler.UnobservedTaskException
. السلوك الافتراضي للاستثناء الذي تم استلامه هو كتابة السجل كاستثناء. يمكن تغيير مستوى السجل باستخدام UniTaskScheduler.UnobservedExceptionWriteLogType
. إذا كنت ترغب في استخدام السلوك المخصص ، فقم بتعيين إجراء على UniTaskScheduler.UnobservedTaskException.
وأيضًا OperationCanceledException
هو استثناء خاص ، يتم تجاهل هذا بصمت في UnobservedTaskException
.
إذا كنت ترغب في إلغاء السلوك في طريقة Unitask Async ، فأرمي 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 ;
}
}
رميات/catch OperationCanceledException
ثقيلة بعض الشيء ، لذلك إذا كان الأداء مصدر قلق ، فاستخدم UniTask.SuppressCancellationThrow
لتجنب رمي التشغيل CanceledException. يعود (bool IsCanceled, T Result)
بدلاً من الرمي.
var ( isCanceled , _ ) = await UniTask . DelayFrame ( 10 , cancellationToken : cts . Token ) . SuppressCancellationThrow ( ) ;
if ( isCanceled )
{
// ...
}
ملاحظة: قمع الرميات فقط إذا قمت بالاتصال مباشرة في الطريقة الأكثر مصدرًا. خلاف ذلك ، سيتم تحويل قيمة الإرجاع ، لكن خط الأنابيب بأكمله لن يقمع الرميات.
بعض الميزات التي تستخدم حلقة لاعب Unity ، مثل UniTask.Yield
و UniTask.Delay
etc ، تحدد حالة cancellationToken على حلقة اللاعب. هذا يعني أنه لا CancellationToken
فورًا عند إطلاقه.
إذا كنت ترغب في تغيير هذا السلوك ، فإن الإلغاء ليكون فوريًا ، قم بتعيين العلم cancelImmediately
كوسيطة.
await UniTask . Yield ( cancellationToken , cancelImmediately : true ) ;
ملاحظة: يعد إعداد cancelImmediately
إلى True واكتشاف الإلغاء الفوري أكثر تكلفة من السلوك الافتراضي. هذا لأنه يستخدم CancellationToken.Register
؛ إنه أثقل من التحقق من cancellationToken على حلقة اللاعب.
المهلة هي تباين في الإلغاء. يمكنك تعيين مهلة من خلال CancellationTokenSouce.CancelAfterSlim(TimeSpan)
وتمرير CancellationToken إلى أساليب Async.
var cts = new CancellationTokenSource ( ) ;
cts . CancelAfterSlim ( TimeSpan . FromSeconds ( 5 ) ) ; // 5sec timeout.
try
{
await UnityWebRequest . Get ( " http://foo " ) . SendWebRequest ( ) . WithCancellation ( cts . Token ) ;
}
catch ( OperationCanceledException ex )
{
if ( ex . CancellationToken == cts . Token )
{
UnityEngine . Debug . Log ( " Timeout " ) ;
}
}
CancellationTokenSouce.CancelAfter
هو واجهة برمجة تطبيقات قياسية. ولكن في الوحدة يجب ألا تستخدمه لأنه يعتمد على مؤقت الترابط.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. " ) ;
}
}
تحسين لتقليل تخصيص CanCellationTokEnsource لفرض المهلة لكل مكالمة ، يمكنك استخدام TimeoutController
في 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 " ) ;
}
}
}
إذا كنت ترغب في استخدام مهلة مع مصدر آخر للإلغاء ، فاستخدم 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
إلى هذه الطريقة ، فسيكون ذلك من داخل المهمة ، لذلك من الممكن إيقاف مهمة التشغيل.
بعض عمليات ASYNC للوحدة لها 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 على لاعب مخصص. أساليب Unitask المستندة إلى اللاعبين (مثل Delay
، DelayFrame
، asyncOperation.ToUniTask
، إلخ ...) تقبل هذا 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
}
إنه يشير إلى موعد التشغيل ، يمكنك التحقق من playllooplist.md إلى PlayerLoop الافتراضي الخاص بـ Unity وحققت حلقة Unitask المخصصة.
يشبه PlayerLoopTiming.Update
yield return null
في coroutine ، ولكن يطلق عليه قبل التحديث (تحديث وأحداث ugui (button.onclick ، etc ...) على ScriptRunBehaviourUpdate
، يتم استدعاء العائد null null على ScriptRunDelayedDynamicFrameRate
). يشبه PlayerLoopTiming.FixedUpdate
WaitForFixedUpdate
.
PlayerLoopTiming.LastPostLateUpdate
لا يعادلyield return new WaitForEndOfFrame()
. يبدو أن Waitforendofframe من Coroutine يعمل بعد الانتهاء من اللاعب. بعض الطرق التي تتطلب نهاية Coroutine من الإطار (Texture2D.ReadPixels
،ScreenCapture.CaptureScreenshotAsTexture
،CommandBuffer
، وما إلى ذلك) لا تعمل بشكل صحيح عند استبدالها بـ Async/في انتظار. في هذه الحالات ، تمرير Monobehaviour (Coroutine Runnner) إلىUniTask.WaitForEndOfFrame
. على سبيل المثال ،await UniTask.WaitForEndOfFrame(this);
هو بديل خفيفة الوزن خالية منyield return new WaitForEndOfFrame()
.ملاحظة: في الوحدة 2023.1 أو الأحدث ،
await UniTask.WaitForEndOfFrame();
لم يعد يتطلب monobehaviour. ويستخدمUnityEngine.Awaitable.EndOfFrameAsync
.
yield return null
و UniTask.Yield
متشابهة ولكن مختلفة. yield return null
يعيد دائمًا الإطار التالي ولكن UniTask.Yield
يعود بعد ذلك. وهذا هو ، call UniTask.Yield(PlayerLoopTiming.Update)
على PreUpdate
، فإنه يعيد نفس الإطار. يضمن UniTask.NextFrame()
إرجاع الإطار التالي ، يمكنك أن تتوقع أن يتصرف هذا تمامًا مثل yield return null
.
Unitask.yield (بدون cancellationToken) هو نوع خاص ، ويعود إلى
YieldAwaitable
ويعمل على yieldrunner. هذا هو الأكثر خفيفة الوزن وأسرع.
يتم إرجاع AsyncOperation
من التوقيت الأصلي. على سبيل المثال ، يتم إرجاع Await SceneManager.LoadSceneAsync
من EarlyUpdate.UpdatePreloading
وبعد استدعاءها ، يتم استدعاء Start
المشهد المحمّل من EarlyUpdate.ScriptRunDelayedStartupFrame
. كما تم إرجاع await UnityWebRequest
من EarlyUpdate.ExecuteMainThreadJobs
.
في Unitask ، تستخدم في انتظار التوقيت الأصلي مباشرة ، في حين تستخدم WithCancellation
و ToUniTask
توقيتًا محددًا. عادةً ما لا تكون هذه مشكلة معينة ، ولكن مع LoadSceneAsync
، فإنه يسبب ترتيبًا مختلفًا للبدء والاستمرار بعد الانتظار. لذلك يوصى بعدم استخدام LoadSceneAsync.ToUniTask
.
ملاحظة: عند استخدام الوحدة 2023.1 أو الأحدث ، تأكد من
using UnityEngine;
في بيانات استخدام ملفك عند العمل مع NewUnityEngine.Awaitable
الأساليب مثلSceneManager.LoadSceneAsync
. هذا يمنع أخطاء التجميع عن طريق تجنب استخدام إصدارUnityEngine.AsyncOperation
.
في StackTrace ، يمكنك التحقق من مكان تشغيله في PlayerLoop.
بشكل افتراضي ، تتم تهيئة PlayerLoop في Unitask على [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
الوحدة ، فإن ذلك يعيد تعيين حلقة اللاعب المخصصة للتخلف عن BeforeSceneLoad
وحقن حلقة ECS. عندما تستدعي Unity طريقة حقن ECS بعد طريقة تهيئة Unitask ، لن تعمل Unitask.
لحل هذه المشكلة ، يمكنك إعادة تخصيص Unitask PlayerLoop بعد تهيئة ECS.
// Get ECS Loop.
var playerLoop = ScriptBehaviourUpdateOrder . CurrentPlayerLoop ;
// Setup UniTask's PlayerLoop.
PlayerLoopHelper . Initialize ( ref playerLoop ) ;
يمكنك تشخيص ما إذا كانت حلقة Player في Unitask جاهزة عن طريق الاتصال بـ PlayerLoopHelper.IsInjectedUniTaskPlayerLoop()
. وأيضًا PlayerLoopHelper.DumpCurrentPlayerLoop
يسجل جميع اللاعبين الحاليين إلى وحدة التحكم.
void Start ( )
{
UnityEngine . Debug . Log ( " UniTaskPlayerLoop ready? " + PlayerLoopHelper . IsInjectedUniTaskPlayerLoop ( ) ) ;
PlayerLoopHelper . DumpCurrentPlayerLoop ( ) ;
}
يمكنك تحسين التكلفة حلقة قليلاً عن طريق إزالة حقن غير مستخدمة لاعب. يمكنك استدعاء PlayerLoopHelper.Initialize(InjectPlayerLoopTimings)
على التهيئة.
var loop = PlayerLoop . GetCurrentPlayerLoop ( ) ;
PlayerLoopHelper . Initialize ( ref loop , InjectPlayerLoopTimings . Minimum ) ; // minimum is Update | FixedUpdate | LastPostLateUpdate
تمتلك InjectPlayerLoopTimings
ثلاثة مسبقًا ، All
Standard
(كل ذلك بدون LastPostLateUpdate) ، Minimum
( Update | FixedUpdate | LastPostLateUpdate
). الافتراضي هو كل شيء ويمكنك الجمع بين توقيت الحقن المخصص مثل InjectPlayerLoopTimings.Update | InjectPlayerLoopTimings.FixedUpdate | InjectPlayerLoopTimings.PreLateUpdate
.
يمكنك ارتكاب خطأ لاستخدام PlayerLoopTiming
غير المحتملة بواسطة microsoft.codeanalysis.bludeApianalyzers. على سبيل المثال ، يمكنك إعداد 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 ( ) ;
}
لاستخدام 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.
}
}
مفيد للتحقق (تسرب) الوحدات. يمكنك فتح نافذة Tracker في Window -> UniTask Tracker
.
يهدف UnitaskTracker لاستخدام تصحيح الأخطاء فقط لأن تمكين التتبع والتقاط StackTraces مفيد ولكن له تأثير كبير في الأداء. الاستخدام الموصى به هو تمكين كل من التتبع و stacktraces للعثور على تسرب المهام وتعطيلها عند الانتهاء.
بشكل افتراضي ، يدعم Unitask TextMeshpro ( BindTo(TMP_Text)
و TMP_InputField
امتدادات الأحداث مثل ugui InputField
القياسية) ، و Dotween ( Tween
كما يمكن انتظاره) والعنوان ( AsyncOperationHandle
و AsyncOperationHandle<T>
على النحو).
يتم تعريفه في ASMDEFs منفصلة مثل UniTask.TextMeshPro
، UniTask.DOTween
، UniTask.Addressables
.
يتم تمكين TextMeshpro و Edlectables Support تلقائيًا عند استيراد الحزم من Package Manager. ومع ذلك ، بالنسبة للدعم بين الأصول ، بعد الاستيراد من الأصول بين الأصول وتحديد البرمجة النصية ، حدد رمز 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
). إنه يعمل على كلا الكامل (صحيح/خطأ) ويقتل (صواب/خطأ). ولكن إذا كنت ترغب في إعادة استخدام tweens ( SetAutoKill(false)
) ، فهذا لا يعمل كما هو متوقع. إذا كنت ترغب في انتظار توقيت آخر ، فإن طرق التمديد التالية موجودة في Tween ، AwaitForComplete
، AwaitForPause
، AwaitForPlay
، AwaitForRewind
، AwaitForStepComplete
.
Unity 2020.2 يدعم C# 8.0 حتى تتمكن من استخدام await foreach
. هذا هو تدوين التحديث الجديد في عصر Async.
// 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.
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 ( _ =>
{
} ) ;
Fire and Frese Tort (على سبيل المثال ، معالجة الأحداث) ، يمكنك أيضًا استخدام Subscribe
.
okButton . OnClickAsAsyncEnumerable ( ) . Where ( ( x , i ) => i % 2 == 0 ) . Subscribe ( _ =>
{
} ) ;
يتم تمكين ASYNC LINQ عند using Cysharp.Threading.Tasks.Linq;
، ويتم تعريف UniTaskAsyncEnumerable
في UniTask.Linq
asmdef.
إنه أقرب إلى UNIRX (الامتدادات التفاعلية) ، ولكن UnitaskasyncEnumerable هو تيار غير متزامن يعتمد على السحب ، في حين أن RX كان دفق غير متزامن قائم على الدفع. لاحظ أنه على الرغم من متشابهة ، فإن الخصائص مختلفة والتفاصيل تتصرف بشكل مختلف معهم.
UniTaskAsyncEnumerable
هي نقطة الدخول مثل Enumerable
. بالإضافة إلى عوامل الاستعلام القياسية ، هناك مولدات أخرى للوحدة مثل EveryUpdate
و Timer
و TimerFrame
Interval
IntervalFrame
و EveryValueChanged
. وأضاف أيضًا عوامل الاستعلام الأصلية في Unitask مثل Append
و Prepend
و DistinctUntilChanged
و ToHashSet
و Buffer
و CombineLatest
و Merge
Do
و Never
و ForEachAsync
و Pairwise
و Publish
و Queue
و Return
و SkipUntil
و TakeUntil
و SkipUntilCanceled
و TakeUntilCanceled
و TakeLast
Subscribe
تحتوي الطريقة مع FUNC كوسيطة على ثلاثة أحمال زائدة إضافية ، ***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
طريقة لإنشاء ITERATOR ASYNC مخصص.
// 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
هو إصدار Unitask من ReactiveProperty. طريقة تمديد BindTo
من IUniTaskAsyncEnumerable<T>
لربط قيم الدفق غير المتزامن لمكونات الوحدة (نص/محدد/TMP/TEXT).
var rp = new AsyncReactiveProperty < int > ( 99 ) ;
// AsyncReactiveProperty itself is IUniTaskAsyncEnumerable, you can query by LINQ
rp . ForEachAsync ( x =>
{
Debug . Log ( x ) ;
} , this . GetCancellationTokenOnDestroy ( ) ) . Forget ( ) ;
rp . Value = 10 ; // push 10 to all subscriber
rp . Value = 11 ; // push 11 to all subscriber
// WithoutCurrent ignore initial value
// BindTo bind stream value to unity components.
rp . WithoutCurrent ( ) . BindTo ( this . textComponent ) ;
await rp . WaitAsync ( ) ; // wait until next value set
// also exists ToReadOnlyAsyncReactiveProperty
var rp2 = new AsyncReactiveProperty < int > ( 99 ) ;
var rorp = rp . CombineLatest ( rp2 , ( x , y ) => ( x , y ) ) . ToReadOnlyAsyncReactiveProperty ( CancellationToken . None ) ;
لا يحصل الدفق غير المتزامن من نوع السحب على القيم التالية حتى تكتمل المعالجة غير المتزامنة في التسلسل. هذا يمكن أن يسكب البيانات من أحداث من النوع الدفع مثل الأزرار.
// can not get click event during 3 seconds complete.
await button . OnClickAsAsyncEnumerable ( ) . ForEachAwaitAsync ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
إنه مفيد (منع النقر المزدوج) ولكن ليس مفيدًا في بعض الأحيان.
باستخدام طريقة Queue()
سيقوم أيضًا بتصوير الأحداث أثناء المعالجة غير المتزامنة.
// queued message in asynchronous processing
await button . OnClickAsAsyncEnumerable ( ) . Queue ( ) . ForEachAwaitAsync ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
أو استخدم Subscribe
والنار وتنسى الأسلوب.
button . OnClickAsAsyncEnumerable ( ) . Subscribe ( async x =>
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) ) ;
} ) ;
Channel
هي نفس System.Threading.tasks.channels والتي تشبه قناة Golang.
حاليًا يدعم فقط القنوات غير المحدودة للمنتجات الواحدة. يمكن أن تنشئ بواسطة Channel.CreateSingleConsumerUnbounded<T>()
.
بالنسبة للمنتج ( .Writer
) ، استخدم TryWrite
لدفع القيمة و TryComplete
لإكمال القناة. بالنسبة للمستهلك ( .Reader
) ، استخدم TryRead
و WaitToReadAsync
و ReadAsync
Completion
و ReadAllAsync
لقراءة الرسائل في قائمة الانتظار.
ReadAllAsync
إرجاع IUniTaskAsyncEnumerable<T>
حتى استعلام مشغلي 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 ( ) ;
}
}
الوحدة 6 تقدم النوع الذي يمكن انتظاره ، يمكن انتظاره. وببساطة ، يمكن اعتبار الانتظار مجموعة فرعية من Unitask ، وفي الواقع ، تأثر تصميم Ayefitable بـ Unitask. يجب أن تكون قادرة على التعامل مع الانتظار المستند إلى اللاعب ، والمهام المجمعة ، ودعم الإلغاء باستخدام CancellationToken
بطريقة مماثلة. مع إدراجها في المكتبة القياسية ، قد تتساءل عما إذا كنت ستستمر في استخدام Unitask أو الترحيل إلى الانتظار. هذا دليل موجز.
أولاً ، تعادل الوظيفة التي توفرها الانتظار لما تقدمه Coroutines. بدلاً من yield return
، تستخدم في انتظار ؛ await NextFrameAsync()
يحل محل yield return null
؛ وهناك ما يعادل WaitForSeconds
و EndOfFrame
. ومع ذلك ، هذا هو مدى ذلك. كونه يعتمد على coroutine من حيث الوظيفة ، فإنه يفتقر إلى الميزات القائمة على المهام. في تطوير التطبيق العملي باستخدام ASYNC/في انتظار ، فإن عمليات مثل WhenAll
تكون ضرورية. بالإضافة إلى ذلك ، يتيح Unitask العديد من العمليات المستندة إلى الإطار (مثل DelayFrame
) والتحكم الأكثر مرونة في اللاعب ، والتي لا تتوفر في انتظارها. بالطبع ، لا يوجد نافذة تعقب أيضًا.
لذلك ، أوصي باستخدام Unitask لتطوير التطبيق. Unitask هي مجموعة كبيرة من المنظر وتتضمن العديد من الميزات الأساسية. لتطوير المكتبة ، حيث تريد تجنب التبعيات الخارجية ، فإن استخدام الانتظار كنوع إرجاع للطرق سيكون مناسبًا. يمكن تحويل الانتظار إلى Unitask باستخدام AsUniTask
، لذلك لا توجد مشكلة في التعامل مع الوظائف القائمة على الانتظار داخل مكتبة Unitask. بالطبع ، إذا لم تكن بحاجة للقلق بشأن التبعيات ، فسيكون استخدام Unitask هو الخيار الأفضل حتى لتطوير المكتبة.
يمكن أن تختبر سمة Unity's [UnityTest]
Coroutine (ienumerator) ولكن لا يمكن اختبار Async. UniTask.ToCoroutine
جسور Async/في انتظار Coroutine حتى تتمكن من اختبار أساليب Async.
[ UnityTest ]
public IEnumerator DelayIgnore ( ) => UniTask . ToCoroutine ( async ( ) =>
{
var time = Time . realtimeSinceStartup ;
Time . timeScale = 0.5f ;
try
{
await UniTask . Delay ( TimeSpan . FromSeconds ( 3 ) , ignoreTimeScale : true ) ;
var elapsed = Time . realtimeSinceStartup - time ;
Assert . AreEqual ( 3 , ( int ) Math . Round ( TimeSpan . FromSeconds ( elapsed ) . TotalSeconds , MidpointRounding . ToEven ) ) ;
}
finally
{
Time . timeScale = 1.0f ;
}
} ) ;
تتم كتابة اختبارات الوحدة الخاصة بـ Unitask باستخدام Unity Test Runner و CySharp/RuntimeUnittestToolkit لتكاملها مع CI وتحقق مما إذا كان IL2CPP يعمل.
تعمل معظم طرق Unitask على موضوع واحد (PlayerLoop) ، مع UniTask.Run
فقط ( Task.Run
المكافئ) و UniTask.SwitchToThreadPool
يعمل على تجمع مؤشرات الترابط. إذا كنت تستخدم تجمع مؤشرات الترابط ، فلن يعمل مع WebGL وما إلى ذلك.
تم إهمال UniTask.Run
الآن. يمكنك استخدام UniTask.RunOnThreadPool
بدلاً من ذلك. ونظر أيضًا فيما إذا كان يمكنك استخدام UniTask.Create
أو UniTask.Void
.
يمكنك تحويل Coroutine (ienumerator) إلى Unitask (أو في انتظار مباشرة) ولكن لديها بعض القيود.
WaitForEndOfFrame
/ WaitForFixedUpdate
/ Coroutine
.StartCoroutine
، ويستخدم PlayerLoopTiming
المحدد ويتم تشغيل PlayerLoopTiming.Update
قبل Update
Monobehaviour و StartCoroutine
. إذا كنت تريد تحويل متوافق تمامًا من Coroutine إلى Async ، فاستخدم IEnumerator.ToUniTask(MonoBehaviour coroutineRunner)
الحمل الزائد. ينفذ StartCoroutine على مثيل للسياسة monobehaviour وينتظر أن يكتمل في Unitask.
يمكن أن تعمل Unitask على محرر الوحدة مثل محرر Coroutine. ومع ذلك ، هناك بعض القيود.
DelayType.Realtime
REALTIME الذي ينتظر الوقت المناسب.EditorApplication.update
التوقيت.-batchmode
مع -quit
لأن الوحدة لا تعمل على تشغيل EditorApplication.update
. بدلاً من ذلك ، لا تستخدم -quit
و Get Deflical مع EditorApplication.Exit(0)
. لدى Unitask العديد من واجهات برمجة التطبيقات القياسية التي تشبه المهام. يوضح هذا الجدول ما هي واجهات برمجة التطبيقات البديلة.
استخدم النوع القياسي.
.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 Caches Async Promise بقوة لتحقيق التخصيص صفريًا (للحصول على التفاصيل الفنية ، راجع Post Post Post Unitask V2 - تخصيص صفري غير متزامن/في انتظار الوحدة ، مع LINK غير المتزامن). بشكل افتراضي ، يقوم بتخزين جميع الوعود ، لكن يمكنك تكوين TaskPool.SetMaxPoolSize
إلى قيمتك ، تشير القيمة إلى حجم ذاكرة التخزين المؤقت لكل نوع. TaskPool.GetCacheSizeInfo
إرجاع الكائنات المحزومة بحلول محاكاة مؤقتًا في البلياردو.
foreach ( var ( type , size ) in TaskPool . GetCacheSizeInfo ( ) )
{
Debug . Log ( type + " : " + size ) ;
}
في UnityEditor ، يُظهر Profiler تخصيص برنامج التحويل البرمجي الذي تم إنشاؤه من Asyncstatemachine ولكنه يحدث فقط في بناء التصحيح (التنمية). C# Compiler يولد ASYNCSTATEMACHINE كطبقة على بناء التصحيح وكبنية على بناء الإصدار.
Unity تدعم خيار تحسين الكود ابتداء من 2020.1 (يمين ، تذييل).
يمكنك تغيير تحسين برنامج التحويل البرمجي C# لإصداره لإزالة تخصيص Asyncstatemachine في عمليات التنمية. يمكن أيضًا تعيين خيار التحسين هذا عبر Compilation.CompilationPipeline-codeOptimization
، و Compilation.CodeOptimization
.
يعد التزامن الافتراضي الخاص بـ Unity ( UnitySynchronizationContext
) بمثابة تطبيق ضعيف للأداء. يتجاوز Unitask SynchronizationContext
(و ExecutionContext
) بحيث لا يستخدمه ولكن إذا كان موجودًا في async Task
، فلا يزال يستخدمه. UniTaskSynchronizationContext
هو بديل لـ UnitySynchronizationContext
وهو أفضل للأداء.
public class SyncContextInjecter
{
[ RuntimeInitializeOnLoadMethod ( RuntimeInitializeLoadType . SubsystemRegistration ) ]
public static void Inject ( )
{
SynchronizationContext . SetSynchronizationContext ( new UniTaskSynchronizationContext ( ) ) ;
}
}
هذا خيار اختياري ولا ينصح به دائمًا ؛ UniTaskSynchronizationContext
أقل أداءً من async UniTask
وليس بديلاً كاملًا Unitask. كما أنه لا يضمن التوافق السلوكي الكامل مع UnitySynchronizationContext
.
يتم استضافة مراجع واجهة برمجة تطبيقات Unitask في cysharp.github.io/unitask بواسطة docfx و cysharp/docfxtemplate.
على سبيل المثال ، يمكن رؤية أساليب مصنع Unitask في طرق Unitask#. يمكن رؤية أساليب المصنع/التمديد في UnitaskAsyncEnmerable في طرق UnitaskAsyncEnumerable#.
يتطلب نسخة من الوحدة التي تدعم معلمة استعلام المسار لحزم GIT (الوحدة> = 2019.3.4f1 ، الوحدة> = 2020.1A21). يمكنك إضافة https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask
إلى مدير الحزمة
أو أضف "com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask"
إلى Packages/manifest.json
.
إذا كنت ترغب في تعيين إصدار مستهدف ، #2.1.0
Unitask يستخدم *.*.*
على سبيل المثال https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.1.0
.
لـ .NET Core ، استخدم Nuget.
PM> تثبيت unitask package
إصدار Unitask of .NET Core هو مجموعة فرعية من Unity Unitask مع إزالة الأساليب المعتمدة على اللاعب.
يتم تشغيله بأداء أعلى من المهمة/ValueTask القياسية ، ولكن يجب أن تكون حريصًا على تجاهل ExecutionContext/SynchronizationContext عند استخدامه. لا يعمل AsyncLocal
أيضًا لأنه يتجاهل ExecutionContext.
إذا كنت تستخدم Unitask داخليًا ، ولكن قم بتوفير Valuetask باعتبارها واجهة برمجة تطبيقات خارجية ، فيمكنك كتابتها مثل ما يلي (مستوحى من 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..
}
}
يهدف الإصدار Core Core للسماح للمستخدمين باستخدام Unitask كواجهة عند مشاركة التعليمات البرمجية مع الوحدة (مثل cysharp/magiconion). .NET Core Version of Unitask يتيح مشاركة الكود السلس.
يتم توفير أساليب الأداة المساعدة مثل عندما يتم توفير CyshARP/ValuetAssupsupples.
هذه المكتبة تحت رخصة معهد ماساتشوستس للتكنولوجيا.