Cập nhật: Năm 2018, Unity tung ra Hệ thống công việc C # như một cách để giảm tải công việc và sử dụng nhiều lõi CPU.
Câu trả lời dưới đây có trước hệ thống này. Nó vẫn sẽ hoạt động, nhưng có thể có các tùy chọn tốt hơn có sẵn trong Unity hiện đại, tùy thuộc vào nhu cầu của bạn. Cụ thể, hệ thống công việc dường như giải quyết một số hạn chế về những chủ đề được tạo thủ công có thể truy cập một cách an toàn, được mô tả bên dưới. Các nhà phát triển thử nghiệm với báo cáo xem trước thực hiện các chương trình phát sóng và xây dựng các lưới song song , ví dụ.
Tôi muốn mời người dùng có kinh nghiệm sử dụng hệ thống công việc này để thêm câu trả lời của riêng họ phản ánh trạng thái hiện tại của động cơ.
Trước đây, tôi đã sử dụng phân luồng cho các tác vụ nặng trong Unity (thường là xử lý hình ảnh & hình học) và nó không khác nhiều so với sử dụng các luồng trong các ứng dụng C # khác, với hai cảnh báo:
Bởi vì Unity sử dụng một tập hợp con cũ hơn của .NET, nên có một số tính năng và thư viện luồng mới hơn mà chúng ta không thể sử dụng, nhưng những điều cơ bản đều có ở đó.
Như Almo lưu ý trong một nhận xét ở trên, nhiều loại Unity không phải là chủ đề an toàn và sẽ đưa ra ngoại lệ nếu bạn cố gắng xây dựng, sử dụng hoặc thậm chí so sánh chúng với luồng chính. Những điều cần lưu ý:
Một trường hợp phổ biến là kiểm tra xem liệu tham chiếu GameObject hoặc Monobehaviour có bị hủy hay không trước khi thử truy cập các thành viên của nó. myUnityObject == null
gọi một toán tử bị quá tải cho bất cứ thứ gì xuất phát từ UnityEngine.Object, nhưng System.Object.ReferenceEquals()
hoạt động ở mức độ này - chỉ cần nhớ rằng GameObject của Dest () so sánh với null bằng cách sử dụng quá tải, nhưng chưa phải là ReferenceEqual thành null.
Đọc các tham số từ các loại Unity thường an toàn trên một luồng khác (trong đó nó sẽ không ngay lập tức đưa ra một ngoại lệ miễn là bạn cẩn thận kiểm tra null như trên), nhưng lưu ý cảnh báo của Philipp ở đây rằng luồng chính có thể đang sửa đổi trạng thái trong khi bạn đang đọc nó Bạn sẽ cần phải xử lý kỷ luật về việc ai được phép sửa đổi những gì và khi nào để tránh đọc một số trạng thái không nhất quán, điều này có thể dẫn đến các lỗi có thể rất khó theo dõi vì chúng phụ thuộc vào thời gian dưới một phần nghìn giây giữa các luồng mà chúng ta có thể 'T tái sản xuất theo ý muốn.
Thành viên tĩnh ngẫu nhiên và thời gian không có sẵn. Tạo một thể hiện của System.Random trên mỗi luồng nếu bạn cần ngẫu nhiên và System.Diagnostics.Stopwatch nếu bạn cần thông tin về thời gian.
Các hàm Mathf, Vector, Ma trận, Đệ tứ và các cấu trúc màu đều hoạt động tốt trên các luồng, do đó bạn có thể thực hiện hầu hết các tính toán của mình một cách riêng biệt
Tạo GameObjects, đính kèm Monobehaviours hoặc tạo / cập nhật Hoạ tiết, Lưới, Vật liệu, v.v ... tất cả đều cần phải xảy ra trên luồng chính. Trước đây khi tôi cần làm việc với những thứ này, tôi đã thiết lập một hàng đợi nhà sản xuất, nơi luồng công nhân của tôi chuẩn bị dữ liệu thô (như một mảng lớn các vectơ / màu sắc để áp dụng cho lưới hoặc kết cấu), và Cập nhật hoặc Coroutine trên các cuộc thăm dò chủ đề chính cho dữ liệu và áp dụng nó.
Với những lưu ý đó, đây là mẫu tôi thường sử dụng cho công việc theo luồng. Tôi không đảm bảo rằng đó là một phong cách thực hành tốt nhất, nhưng nó hoàn thành công việc. (Nhận xét hoặc chỉnh sửa để cải thiện đều được chào đón - Tôi biết luồng là một chủ đề rất sâu sắc mà tôi chỉ biết những điều cơ bản)
using UnityEngine;
using System.Threading;
public class MyThreadedBehaviour : MonoBehaviour
{
bool _threadRunning;
Thread _thread;
void Start()
{
// Begin our heavy work on a new thread.
_thread = new Thread(ThreadedWork);
_thread.Start();
}
void ThreadedWork()
{
_threadRunning = true;
bool workDone = false;
// This pattern lets us interrupt the work at a safe point if neeeded.
while(_threadRunning && !workDone)
{
// Do Work...
}
_threadRunning = false;
}
void OnDisable()
{
// If the thread is still running, we should shut it down,
// otherwise it can prevent the game from exiting correctly.
if(_threadRunning)
{
// This forces the while loop in the ThreadedWork function to abort.
_threadRunning = false;
// This waits until the thread exits,
// ensuring any cleanup we do after this is safe.
_thread.Join();
}
// Thread is guaranteed no longer running. Do other cleanup tasks.
}
}
Nếu bạn không nhất thiết phải phân chia công việc giữa các luồng để tăng tốc và bạn chỉ đang tìm cách làm cho nó không bị chặn để phần còn lại của trò chơi của bạn tiếp tục tích tắc, một giải pháp nhẹ hơn trong Unity là Coroutines . Đây là những chức năng có thể thực hiện một số công việc sau đó mang lại quyền kiểm soát cho động cơ để tiếp tục những gì nó đang làm và tiếp tục hoạt động liên tục sau đó.
using UnityEngine;
using System.Collections;
public class MyYieldingBehaviour : MonoBehaviour
{
void Start()
{
// Begin our heavy work in a coroutine.
StartCoroutine(YieldingWork());
}
IEnumerator YieldingWork()
{
bool workDone = false;
while(!workDone)
{
// Let the engine run for a frame.
yield return null;
// Do Work...
}
}
}
Điều này không cần bất kỳ cân nhắc dọn dẹp đặc biệt nào, vì động cơ (theo như tôi có thể nói) đã loại bỏ các xác chết khỏi các vật thể bị phá hủy cho bạn.
Tất cả trạng thái cục bộ của phương thức được bảo toàn khi nó sinh ra và tiếp tục, vì vậy trong nhiều mục đích, nó như thể nó đang chạy không bị gián đoạn trên một luồng khác (nhưng bạn có tất cả các tiện ích để chạy trên luồng chính). Bạn chỉ cần đảm bảo rằng mỗi lần lặp của nó đủ ngắn để nó không làm chậm luồng chính của bạn một cách vô lý.
Bằng cách đảm bảo các hoạt động quan trọng không bị tách biệt bởi một sản lượng, bạn có thể có được sự thống nhất của hành vi đơn luồng - biết rằng không có tập lệnh hoặc hệ thống nào khác trên luồng chính có thể sửa đổi dữ liệu mà bạn đang làm việc.
Dòng lợi nhuận mang lại cho bạn một vài lựa chọn. Bạn có thể...
... tùy thuộc vào thời điểm bạn muốn coroutine thực hiện lượt tiếp theo.
Hoặc yield break;
để ngăn chặn coroutine trước khi nó kết thúc, cách bạn có thể sử dụng return;
để sớm ra khỏi một phương pháp thông thường.