Đầu tiên, hãy nói về coroutines. Chúng được giới thiệu trong Unity vào năm 2011, thậm chí trước khi async/await xuất hiện trong .NET. Trong Unity, các coroutine cho phép chúng ta thực hiện một tập hợp các hướng dẫn trên nhiều khung, thay vì thực hiện tất cả chúng cùng một lúc. Chúng tương tự như các luồng, nhưng nhẹ hơn và được tích hợp vào vòng cập nhật của Unity, khiến chúng rất phù hợp để phát triển trò chơi.
Để tạo một coroutine, bạn cần khai báo một hàm với kiểu trả về IEnumerator
. Hàm này có thể chứa bất kỳ logic nào mà bạn muốn coroutine thực thi.
Để bắt đầu một coroutine, bạn cần gọi phương thức StartCoroutine
trên một cá thể MonoBehaviour
và chuyển hàm coroutine làm đối số:
public class Example : MonoBehaviour { void Start() { StartCoroutine(MyCoroutine()); } IEnumerator MyCoroutine() { Debug.Log("Starting coroutine"); yield return null; Debug.Log("Executing coroutine"); yield return null; Debug.Log("Finishing coroutine"); } }
Có một số hướng dẫn năng suất có sẵn trong Unity, chẳng hạn như WaitForSeconds
, WaitForEndOfFrame
, WaitForFixedUpdate
, WaitForSecondsRealtime
, WaitUntil
cũng như một số hướng dẫn khác. Điều quan trọng cần nhớ là việc sử dụng chúng dẫn đến phân bổ, vì vậy chúng nên được sử dụng lại bất cứ khi nào có thể.
Ví dụ: xem xét phương pháp này từ tài liệu:
IEnumerator Fade() { Color c = renderer.material.color; for (float alpha = 1f; alpha >= 0; alpha -= 0.1f) { ca = alpha; renderer.material.color = c; yield return new WaitForSeconds(.1f); } }
Với mỗi lần lặp lại vòng lặp, một phiên bản mới của new WaitForSeconds(.1f)
sẽ được tạo. Thay vì điều này, chúng ta có thể di chuyển việc tạo ra bên ngoài vòng lặp và tránh phân bổ:
IEnumerator Fade() { Color c = renderer.material.color; **var waitForSeconds = new WaitForSeconds(0.2f);** for (float alpha = 1f; alpha >= 0; alpha -= 0.1f) { ca = alpha; renderer.material.color = c; yield return **waitForSeconds**; } }
Một thuộc tính quan trọng khác cần lưu ý là yield return
có thể được sử dụng với tất cả các phương thức Async
do Unity cung cấp vì AsyncOperation
s là hậu duệ của YieldInstruction
:
yield return SceneManager.LoadSceneAsync("path/to/scene.unity");
MonoBehaviour
khởi chạy nó. Nếu GameObject
bị tắt hoặc bị hủy, coroutine sẽ ngừng xử lý.try-catch-finally
không thể được sử dụng do sự hiện diện của cú pháp năng suất.yield return
trước khi mã tiếp theo bắt đầu thực thi.Lời hứa là một mẫu để tổ chức và làm cho các hoạt động không đồng bộ dễ đọc hơn. Chúng đã trở nên phổ biến do được sử dụng trong nhiều thư viện JavaScript của bên thứ ba và kể từ ES6, chúng đã được triển khai nguyên bản.
Cách chính để tương tác với Promise là thông qua các hàm gọi lại .
Theo này từ chức Promises/A+, một Promise có thể ở một trong ba trạng thái:
Pending
: trạng thái ban đầu, điều này có nghĩa là hoạt động không đồng bộ vẫn đang được tiến hành và kết quả của hoạt động vẫn chưa được biết.Fulfilled
( Resolved
): trạng thái đã giải quyết được kèm theo một giá trị đại diện cho kết quả của hoạt động.Rejected
: nếu hoạt động không đồng bộ không thành công vì bất kỳ lý do gì, Lời hứa được cho là "bị từ chối". Trạng thái bị từ chối kèm theo lý do thất bại.
var promise = MakeRequest("//some.api") .Then(response => Parse(response)) .Then(result => OnRequestSuccess(result)) .Then(() => PlaySomeAnimation()) .Catch(exception => OnRequestFailed(exception));
public IPromise<string> MakeRequest(string url) { // Create a new promise object var promise = new Promise<string>(); // Create a new web client using var client = new WebClient(); // Add a handler for the DownloadStringCompleted event client.DownloadStringCompleted += (sender, eventArgs) => { // If an error occurred, reject the promise if (eventArgs.Error != null) { promise.Reject(eventArgs.Error); } // Otherwise, resolve the promise with the result else { promise.Resolve(eventArgs.Result); } }; // Start the download asynchronously client.DownloadStringAsync(new Uri(url), null); // Return the promise return promise; }
Chúng ta cũng có thể bọc các coroutine trong một Promise
:
void Start() { // Load the scene and then show the intro animation LoadScene("path/to/scene.unity") .Then(() => ShowIntroAnimation()) .Then( ... ); } // Load a scene and return a promise Promise LoadScene(string sceneName) { // Create a new promise var promise = new Promise(); // Start a coroutine to load the scene StartCoroutine(LoadSceneRoutine(promise, sceneName)); // Return the promise return promise; } IEnumerator LoadSceneRoutine(Promise promise, string sceneName) { // Load the scene asynchronously yield return SceneManager.LoadSceneAsync(sceneName); // Resolve the promise once the scene is loaded promise.Resolve(); }
Và tất nhiên, bạn có thể tổ chức bất kỳ sự kết hợp nào của thứ tự thực hiện lời hứa bằng cách sử dụng ThenAll
/ Promise.All
và ThenRace
/ Promise.Race
:
// Execute the following two promises in sequence Promise.Sequence( () => Promise.All( // Execute the two promises in parallel RunAnimation("Foo"), PlaySound("Bar") ), () => Promise.Race( // Execute the two promises in a race RunAnimation("One"), PlaySound("Two") ) );
Trong lịch sử của .NET, có thể phân biệt các giai đoạn sau:
BeginSmth
trả về giao diện IAsyncResult
. Phương thức EndSmth
lấy IAsyncResult
; nếu hoạt động không được hoàn thành tại thời điểm cuộc gọi EndSmth
, luồng sẽ bị chặn.Task
và Task<TResult>
.
Để tạo một phương thức không đồng bộ, phương thức đó phải được đánh dấu bằng từ khóa async
, chứa một await
bên trong và giá trị trả về phải là Task
, Task<T>
hoặc void
(không được khuyến nghị).
public async Task Example() { SyncMethodA(); await Task.Delay(1000); // the first async operation SyncMethodB(); await Task.Delay(2000); // the second async operation SyncMethodC(); await Task.Delay(3000); // the third async operation }
Trong ví dụ này, việc thực thi sẽ diễn ra như sau:
SyncMethodA
) sẽ được thực thi.await Task.Delay(1000)
được khởi chạy và dự kiến sẽ được thực thi. Trong khi đó, mã được gọi khi hoạt động không đồng bộ hoàn tất ("tiếp tục") sẽ được lưu.SyncMethodB
) sẽ bắt đầu thực thi.await Task.Delay(2000)
) được khởi chạy và dự kiến sẽ được thực thi. Đồng thời, phần tiếp theo — mã sau thao tác không đồng bộ thứ hai ( SyncMethodC
) sẽ được giữ nguyên.SyncMethodC
sẽ được thực thi, tiếp theo là thực thi và chờ hoạt động không đồng bộ thứ ba await Task.Delay(3000)
.
Bạn cũng có thể tổ chức bất kỳ tổ hợp lệnh thực thi nào bằng cách sử dụng WhenAll
và WhenAny
:
var allTasks = Task.WhenAll( Task.Run(() => { /* ... */ }), Task.Run(() => { /* ... */ }), Task.Run(() => { /* ... */ }) ); allTasks.ContinueWith(t => { Console.WriteLine("All the tasks are completed"); }); var anyTask = Task.WhenAny( Task.Run(() => { /* ... */ }), Task.Run(() => { /* ... */ }), Task.Run(() => { /* ... */ }) ); anyTask.ContinueWith(t => { Console.WriteLine("One of tasks is completed"); });
Trình biên dịch C# chuyển đổi các cuộc gọi không đồng bộ/chờ đợi thành một máy trạng thái IAsyncStateMachine
, đây là một tập hợp các hành động tuần tự phải được thực hiện để hoàn thành thao tác không đồng bộ.
Do đó, phương thức Example
được chuyển thành tạo và khởi tạo một máy trạng thái với chú thích [AsyncStateMachine(typeof(ExampleStateMachine))]
và bản thân máy trạng thái có một số trạng thái bằng với số lần gọi chờ.
Ví dụ về phương thức được biến đổi Example
[AsyncStateMachine(typeof(ExampleStateMachine))] public /*async*/ Task Example() { // Create a new instance of the ExampleStateMachine class ExampleStateMachine stateMachine = new ExampleStateMachine(); // Create a new AsyncTaskMethodBuilder and assign it to the taskMethodBuilder property of the stateMachine instance stateMachine.taskMethodBuilder = AsyncTaskMethodBuilder.Create(); // Set the currentState property of the stateMachine instance to -1 stateMachine.currentState = -1; // Start the stateMachine instance stateMachine.taskMethodBuilder.Start(ref stateMachine); // Return the Task property of the taskMethodBuilder return stateMachine.taskMethodBuilder.Task; }
Ví dụ về máy trạng thái được tạo ExampleStateMachine
[CompilerGenerated] private sealed class ExampleStateMachine : IAsyncStateMachine { public int currentState; public AsyncTaskMethodBuilder taskMethodBuilder; private TaskAwaiter taskAwaiter; public int paramInt; private int localInt; void IAsyncStateMachine.MoveNext() { int num = currentState; try { TaskAwaiter awaiter3; TaskAwaiter awaiter2; TaskAwaiter awaiter; switch (num) { default: localInt = paramInt; // Call the first synchronous method SyncMethodA(); // Create a task awaiter for a delay of 1000 milliseconds awaiter3 = Task.Delay(1000).GetAwaiter(); // If the task is not completed, set the current state to 0 and store the awaiter if (!awaiter3.IsCompleted) { currentState = 0; taskAwaiter = awaiter3; // Store the current state machine ExampleStateMachine stateMachine = this; // Await the task and pass the state machine taskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter3, ref stateMachine); return; } // If the task is completed, jump to the label after the first await goto Il_AfterFirstAwait; case 0: // Retrieve the awaiter from the taskAwaiter field awaiter3 = taskAwaiter; // Reset the taskAwaiter field taskAwaiter = default(TaskAwaiter); currentState = -1; // Jump to the label after the first await goto Il_AfterFirstAwait; case 1: // Retrieve the awaiter from the taskAwaiter field awaiter2 = taskAwaiter; // Reset the taskAwaiter field taskAwaiter = default(TaskAwaiter); currentState = -1; // Jump to the label after the second await goto Il_AfterSecondAwait; case 2: // Retrieve the awaiter from the taskAwaiter field awaiter = taskAwaiter; // Reset the taskAwaiter field taskAwaiter = default(TaskAwaiter); currentState = -1; break; Il_AfterFirstAwait: awaiter3.GetResult(); // Call the second synchronous method SyncMethodB(); // Create a task awaiter for a delay of 2000 milliseconds awaiter2 = Task.Delay(2000).GetAwaiter(); // If the task is not completed, set the current state to 1 and store the awaiter if (!awaiter2.IsCompleted) { currentState = 1; taskAwaiter = awaiter2; // Store the current state machine ExampleStateMachine stateMachine = this; // Await the task and pass the state machine taskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine); return; } // If the task is completed, jump to the label after the second await goto Il_AfterSecondAwait; Il_AfterSecondAwait: // Get the result of the second awaiter awaiter2.GetResult(); // Call the SyncMethodC SyncMethodC(); // Create a new awaiter with a delay of 3000 milliseconds awaiter = Task.Delay(3000).GetAwaiter(); // If the awaiter is not completed, set the current state to 2 and store the awaiter if (!awaiter.IsCompleted) { currentState = 2; taskAwaiter = awaiter; // Set the stateMachine to this ExampleStateMachine stateMachine = this; // Await the task and pass the state machine taskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); return; } break; } // Get the result of the awaiter awaiter.GetResult(); } catch (Exception exception) { currentState = -2; taskMethodBuilder.SetException(exception); return; } currentState = -2; taskMethodBuilder.SetResult(); } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { /*...*/ } }
Trong lệnh gọi AwaitUnsafeOnCompleted
, bối cảnh đồng SynchronizationContext
hóa hiện tại sẽ được lấy. SynchronizationContext là một khái niệm trong C# được sử dụng để biểu diễn ngữ cảnh kiểm soát việc thực hiện một tập hợp các hoạt động không đồng bộ. Nó được sử dụng để điều phối việc thực thi mã trên nhiều luồng và để đảm bảo rằng mã được thực thi theo một thứ tự cụ thể. Mục đích chính của SynchronizationContext là cung cấp một cách để kiểm soát việc lên lịch và thực hiện các hoạt động không đồng bộ trong môi trường đa luồng.
Trong các môi trường khác nhau, SynchronizationContext
có các triển khai khác nhau. Ví dụ, trong .NET, có:
System.Windows.Threading.DispatcherSynchronizationContext
System.Windows.Forms.WindowsFormsSynchronizationContext
System.Threading.WinRTSynchronizationContext
System.Web.AspNetSynchronizationContext
Unity cũng có bối cảnh đồng bộ hóa riêng, UnitySynchronizationContext
, cho phép chúng tôi sử dụng các hoạt động không đồng bộ với liên kết với API PlayerLoop. Ví dụ mã sau đây cho thấy cách xoay một đối tượng trong mỗi khung bằng cách sử dụng Task.Yield()
:
private async void Start() { while (true) { transform.Rotate(0, Time.deltaTime * 50, 0); await Task.Yield(); } }
using UnityEngine; using System.Net.Http; using System.Threading.Tasks; public class NetworkRequestExample : MonoBehaviour { private async void Start() { string response = await GetDataFromAPI(); Debug.Log("Response from API: " + response); } private async Task<string> GetDataFromAPI() { using (var client = new HttpClient()) { var response = await client.GetStringAsync("//api.example.com/data"); return response; } } }
Nhờ UnitySynchronizationContext
, chúng ta có thể sử dụng các phương thức UnityEngine
một cách an toàn (chẳng hạn như Debug.Log()
) ngay sau khi hoàn thành thao tác không đồng bộ, vì việc thực thi mã này sẽ tiếp tục trong chuỗi Unity chính.
Lớp này cho phép bạn quản lý một đối tượng Task
. Nó được tạo ra để điều chỉnh các phương thức không đồng bộ cũ thành TAP, nhưng nó cũng rất hữu ích khi chúng ta muốn bao bọc một Task
xung quanh một số hoạt động chạy dài dựa trên một số sự kiện.
Trong ví dụ sau, đối tượng Task
bên trong taskCompletionSource
sẽ hoàn thành sau 3 giây kể từ khi bắt đầu và chúng ta sẽ nhận được kết quả của nó trong phương thức Update
:
using System.Threading.Tasks; using UnityEngine; public class Example : MonoBehaviour { private TaskCompletionSource<int> taskCompletionSource; private void Start() { // Create a new TaskCompletionSource taskCompletionSource = new TaskCompletionSource<int>(); // Start a coroutine to wait 3 seconds // and then set the result of the TaskCompletionSource StartCoroutine(WaitAndComplete()); } private IEnumerator WaitAndComplete() { yield return new WaitForSeconds(3); // Set the result of the TaskCompletionSource taskCompletionSource.SetResult(10); } private async void Update() { // Await the result of the TaskCompletionSource int result = await taskCompletionSource.Task; // Log the result to the console Debug.Log("Result: " + result); } }
Mã hủy bỏ được sử dụng trong C# để báo hiệu rằng một tác vụ hoặc hoạt động sẽ bị hủy bỏ. Mã thông báo được chuyển đến nhiệm vụ hoặc hoạt động và mã trong nhiệm vụ hoặc hoạt động có thể kiểm tra mã thông báo định kỳ để xác định xem có nên dừng nhiệm vụ hoặc hoạt động hay không. Điều này cho phép hủy bỏ một nhiệm vụ hoặc hoạt động một cách gọn gàng và nhẹ nhàng, thay vì chỉ giết chết nó một cách đột ngột.
Mẫu tổng thể tương tự như việc sử dụng TaskCompletionSource
. Đầu tiên, một CancellationTokenSource
được tạo, sau đó Token
của nó được chuyển sang hoạt động không đồng bộ:
public class ExampleMonoBehaviour : MonoBehaviour { private CancellationTokenSource _cancellationTokenSource; private async void Start() { // Create a new CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); // Get the token from the CancellationTokenSource CancellationToken token = _cancellationTokenSource.Token; try { // Start a new Task and pass in the token await Task.Run(() => DoSomething(token), token); } catch (OperationCanceledException) { Debug.Log("Task was cancelled"); } } private void DoSomething(CancellationToken token) { for (int i = 0; i < 100; i++) { // Check if the token has been cancelled if (token.IsCancellationRequested) { // Return if the token has been cancelled return; } Debug.Log("Doing something..."); // Sleep for 1 second Thread.Sleep(1000); } } private void OnDestroy() { // Cancel the token when the object is destroyed _cancellationTokenSource.Cancel(); } }
Khi thao tác bị hủy, một OperationCanceledException
sẽ được ném ra và thuộc tính Task.IsCanceled
sẽ được đặt thành true
.
Điều quan trọng cần lưu ý là các đối tượng Task
được quản lý bởi thời gian chạy .NET chứ không phải bởi Unity và nếu đối tượng thực thi tác vụ bị hủy (hoặc nếu trò chơi thoát khỏi chế độ chơi trong trình chỉnh sửa), thì tác vụ sẽ tiếp tục chạy như Unity có không có cách nào để hủy bỏ nó.
Bạn luôn cần đi kèm với await Task
với CancellationToken
tương ứng. Điều này dẫn đến một số mã dự phòng và trong Unity 2022.2, các mã thông báo tích hợp ở cấp MonoBehaviour
và toàn bộ cấp Application
đã xuất hiện.
Hãy xem ví dụ trước thay đổi như thế nào khi sử dụng destroyCancellationToken
của đối tượng MonoBehaviour
:
using System.Threading; using System.Threading.Tasks; using UnityEngine; public class ExampleMonoBehaviour : MonoBehaviour { private async void Start() { // Get the cancellation token from the MonoBehaviour CancellationToken token = this.destroyCancellationToken; try { // Start a new Task and pass in the token await Task.Run(() => DoSomething(token), token); } catch (OperationCanceledException) { Debug.Log("Task was cancelled"); } } private void DoSomething(CancellationToken token) { for (int i = 0; i < 100; i++) { // Check if the token has been cancelled if (token.IsCancellationRequested) { // Return if the token has been cancelled return; } Debug.Log("Doing something..."); // Sleep for 1 second Thread.Sleep(1000); } } }
Chúng tôi không còn cần phải tạo thủ công CancellationTokenSource
và hoàn thành tác vụ trong phương thức OnDestroy
. Đối với các tác vụ không được liên kết với một MonoBehaviour
cụ thể, chúng ta có thể sử dụng UnityEngine.Application.exitCancellationToken
. Thao tác này sẽ chấm dứt tác vụ khi thoát Chế độ phát (trong Trình chỉnh sửa) hoặc khi thoát ứng dụng.
Task
quá cồng kềnh và gây ra nhiều phân bổ.Task
không khớp với luồng Unity (luồng đơn).
Thư viện bỏ qua những hạn chế này mà không cần sử dụng các luồng hoặc SynchronizationContext
. Nó đạt được sự vắng mặt của phân bổ bằng cách sử dụng loại dựa trên cấu trúc UniTask<T>
.
Ngoài ra, bạn có thể chuyển đổi tất cả AsyncOperations
thành UnitTask
bằng các phương thức mở rộng:
using UnityEngine; using UniTask; public class AssetLoader : MonoBehaviour { public async void LoadAsset(string assetName) { var loadRequest = Resources.LoadAsync<GameObject>(assetName); await loadRequest.AsUniTask(); var asset = loadRequest.asset as GameObject; if (asset != null) { // Do something with the loaded asset } } }
Trong ví dụ này, phương thức LoadAsset
sử dụng Resources.LoadAsync
để tải nội dung không đồng bộ. Sau đó, phương thức AsUniTask
được sử dụng để chuyển đổi AsyncOperation
do LoadAsync
trả về thành UniTask
, có thể chờ đợi.
Như trước đây, bạn có thể tổ chức bất kỳ sự kết hợp thứ tự thực thi nào bằng cách sử dụng UniTask.WhenAll
và UniTask.WhenAny
:
using System.Threading; using Cysharp.Threading.Tasks; using UnityEngine; public class Example : MonoBehaviour { private async void Start() { // Start two Tasks and wait for both to complete await UniTask.WhenAll(Task1(), Task2()); // Start two Tasks and wait for one to complete await UniTask.WhenAny(Task1(), Task2()); } private async UniTask Task1() { // Do something } private async UniTask Task2() { // Do something } }
Trong UniTask, có một triển khai SynchronizationContext
khác gọi là UniTaskSynchronizationContext
có thể được sử dụng để thay thế UnitySynchronizationContext
để có hiệu suất tốt hơn.
Trong phiên bản alpha đầu tiên của Unity 2023.1, lớp Awaitable
đã được giới thiệu. Các Coroutine có thể chờ đợi là các loại giống như Tác vụ không đồng bộ/tương thích với chờ đợi được thiết kế để chạy trong Unity. Không giống như .NET Tasks, chúng được quản lý bởi engine chứ không phải runtime.
private async Awaitable DoSomethingAsync() { // awaiting built-in events await Awaitable.EndOfFrameAsync(); await Awaitable.WaitForSecondsAsync(); // awaiting .NET Tasks await Task.Delay(2000, destroyCancellationToken); await Task.Yield(); // awaiting AsyncOperations await SceneManager.LoadSceneAsync("path/to/scene.unity"); // ... }
Chúng có thể được chờ đợi và được sử dụng làm kiểu trả về của phương thức không đồng bộ. So với System.Threading.Tasks
, chúng ít phức tạp hơn nhưng có các phím tắt nâng cao hiệu suất dựa trên các giả định dành riêng cho Unity.
Awaitable
chỉ có thể được chờ đợi một lần; nó không thể được chờ đợi bởi nhiều chức năng không đồng bộ.Awaiter.GetResults()
sẽ không chặn cho đến khi hoàn thành. Gọi nó trước khi hoạt động kết thúc là hành vi không xác định.ExecutionContext
. Vì lý do bảo mật, Nhiệm vụ .NET nắm bắt ngữ cảnh thực thi khi đang chờ để truyền bá ngữ cảnh mạo danh qua các cuộc gọi không đồng bộ.SynchronizationContext
. Các phần tiếp theo của Coroutine được thực thi đồng bộ từ mã làm tăng phần hoàn thành. Trong hầu hết các trường hợp, đây sẽ là từ khung chính của Unity.ObjectPool
đã được cải thiện để tránh kiểm tra giới hạn Stack<T>
trong các trình tự nhận/phát hành thông thường được tạo bởi các máy trạng thái không đồng bộ.
Để có được kết quả của một thao tác dài, bạn có thể sử dụng loại Awaitable<T>
. Bạn có thể quản lý việc hoàn thành Awaitable
bằng cách sử dụng AwaitableCompletionSource
và AwaitableCompletionSource<T>
, tương tự như TaskCompletitionSource
:
using UnityEngine; using Cysharp.Threading.Tasks; public class ExampleBehaviour : MonoBehaviour { private AwaitableCompletionSource<bool> _completionSource; private async void Start() { // Create a new AwaitableCompletionSource _completionSource = new AwaitableCompletionSource<bool>(); // Start a coroutine to wait 3 seconds // and then set the result of the AwaitableCompletionSource StartCoroutine(WaitAndComplete()); // Await the result of the AwaitableCompletionSource bool result = await _completionSource.Awaitable; // Log the result to the console Debug.Log("Result: " + result); } private IEnumerator WaitAndComplete() { yield return new WaitForSeconds(3); // Set the result of the AwaitableCompletionSource _completionSource.SetResult(true); } }
Đôi khi, cần phải thực hiện các phép tính lớn có thể dẫn đến đóng băng trò chơi. Đối với điều này, tốt hơn là sử dụng các phương thức Có thể chờ đợi: BackgroundThreadAsync()
và MainThreadAsync()
. Chúng cho phép bạn thoát khỏi luồng chính và quay lại luồng đó.
private async Awaitable DoCalculationsAsync() { // Awaiting execution on a ThreadPool background thread. await Awaitable.BackgroundThreadAsync(); var result = PerformSomeHeavyCalculations(); // Awaiting execution on the Unity main thread. await Awaitable.MainThreadAsync(); // Using the result in main thread Debug.Log(result); }
Đoàn kết | quân đoàn | lời hứa | Nhiệm vụ .NET | UniTask | Mã thông báo hủy tích hợp | API có thể chờ đợi |
---|---|---|---|---|---|---|
5.6 | ✅ | ✅ | | | | |
2017.1 | ✅ | ✅ | ✅ | | | |
2018.4 | ✅ | ✅ | ✅ | ✅ | | |
2022.2 | ✅ | ✅ | ✅ | ✅ | ✅ | |
2023.1 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |