Trộn chủ đề và coroutines trong Unity3D Mobile


8

Tôi đã có một coroutine trong Unity3D đã tải xuống một zip từ máy chủ, trích xuất nó vào đường dẫn dữ liệu liên tục và tải nội dung của nó vào bộ nhớ. Dòng chảy trông giống như thế này:

IEnumerator LongCoroutine()
{
    yield return StartCoroutine(DownloadZip());
    ExtractZip();
    yield return StartCoroutine(LoadZipContent());
}

Nhưng ExtractZip()phương thức (sử dụng thư viện DotNetZip), đồng bộ, mất quá nhiều thời gian và khiến tôi không có chỗ để nhường trong quá trình này.

Điều này dẫn đến việc ứng dụng bị giết (trên thiết bị di động) bất cứ khi nào tôi cố trích xuất một zip lớn, mà tôi cho rằng là do luồng chính trở nên không phản hồi quá lâu.

Đây có phải là điều mà hệ điều hành di động được biết là làm (giết ứng dụng nếu một khung hình mất quá nhiều thời gian)?

Vì vậy, tôi giả định rằng trích xuất zip trên một luồng riêng biệt có thể giải quyết vấn đề và dường như nó đã hoạt động. Tôi đã tạo lớp này:

public class ThreadedAction
{
    public ThreadedAction(Action action)
    {
        var thread = new Thread(() => {
            if(action != null)
                action();
            _isDone = true;
        });
        thread.Start();
    }

    public IEnumerator WaitForComplete()
    {
        while (!_isDone)
            yield return null;
    }

    private bool _isDone = false;
}

Và tôi sử dụng nó như thế này:

IEnumerator LongCoroutine()
{
    yield return StartCoroutine(DownloadZip());
    var extractAction = new ThreadedAction(ExtractZip);
    yield return StartCoroutine(extractAction.WaitForComplete());
    yield return StartCoroutine(LoadZipContent());
}

Nhưng tôi vẫn không chắc đây là cách tốt nhất để thực hiện nó hay nếu tôi cần khóa _isDone(không quá quen với đa luồng).

Có thể một cái gì đó sai với điều này / tôi đang thiếu một cái gì đó?

Câu trả lời:


4

Đây là một giải pháp thực sự thanh lịch để gói các tác vụ đa luồng trong một coroutine, được thực hiện tốt :)

Trộn coroutines và thread là hoàn toàn an toàn, miễn là bạn khóa chính xác quyền truy cập vào các tài nguyên được chia sẻ giữa luồng chính của bạn (mà coroutine thực thi trên) và (các) luồng worker mà bạn tạo. Bạn không cần phải khóa _isDone bằng bất kỳ cách nào, vì nó chỉ được ghi bởi luồng công nhân và không có trạng thái trung gian nào có thể khiến luồng chính hoạt động sai.

Nơi bạn cần tìm ra các vấn đề tiềm ẩn, là nếu có bất kỳ tài nguyên nào được ExtractZip ghi vào và

  1. đồng thời được viết bởi một hàm được gọi từ luồng chính của bạn hoặc
  2. được đọc bởi một chức năng trên luồng chính và dự kiến ​​sẽ ở trạng thái an toàn trước khi ExtractZip hoàn thành.

Trong trường hợp cụ thể này, lo lắng của tôi là nếu bạn không kiểm tra rằng bạn không cố tải xuống cùng một tệp vào cùng một vị trí hai lần, bạn có thể có hai luồng chạy đồng thời ExtractZip gây nhiễu lẫn nhau.


1

Có một giải pháp miễn phí cho việc này trên Cửa hàng tài sản:

Ninja chủ đề

Với nó, bạn có thể dễ dàng chuyển đổi giữa chủ đề chính & chủ đề nền, thích:

void Awake() {
    this.StartCoroutineAsync(AsyncCouroutine());
}

IEnumerator AsyncCoroutine() {
    // won't block
    Thread.Sleep(10000);

    yield return Ninja.JumpToUnity;

    // we're now on Unity's main thread
    var time = Time.time;

    yield return Ninja.JumpBack;

    // now on background thread again
    // ...

-1

Chà, tôi cũng không phải là chuyên gia chính, nhưng tôi nghĩ trong ví dụ thứ 2 của bạn, hàm WaitForComplete () vẫn sẽ chặn luồng cuộc gọi cho đến khi luồng ThreadedAction hoàn thành. Những gì bạn có thể làm là chuyển một hàm gọi lại cho luồng xử lý zip của bạn và để nó gọi hàm đó khi hoàn thành. Bằng cách đó, luồng chính của bạn khởi động luồng zip và sau đó tiếp tục thực hiện công việc của mình (cập nhật GUI, bạn có gì) và sau đó chức năng gọi lại sẽ đặt một cái gì đó trong luồng chính khi nó hoàn thành (như boolean zipDone = true), và tại thời điểm này, luồng chính có thể phản ứng với điều đó (hiển thị "Trích xuất Zip" trong GUI, v.v.).


2
Nó sẽ xuất hiện như vậy, nhưng WaitForComplete là một coroutine Unity3D, vì vậy nó sẽ chỉ chạy một vòng lặp trong vòng lặp while mỗi khung hình, sau đó nhường cho mọi thứ khác trong bản cập nhật trò chơi. Nó không chặn chuỗi :)
David Gouveia 13/03/13

-2

Bạn không cần phải khóa _isDone, nhưng nó cần phải được đánh dấu biến động.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.