Birlik'te ana ipliği nasıl dondurmamak?


33

Hesaplamalı olarak ağır bir seviye üretme algoritmasına sahibim. Bu nedenle, onu çağırmak her zaman oyun ekranının donmasına neden olur. Oyun dondurulmuş olmadığını göstermek için bir yükleme ekranı oluşturmaya devam ederken, işlevi ikinci bir iş parçacığına nasıl yerleştirebilirim?


1
Iş parçacığı sistemi arka planda diğer işleri çalıştırmak için kullanabilirsiniz ... ancak bu şeyler iş parçacığı güvenli olmadığından Unity API'de bir şey çağırmayabilirler. Sadece yorum yapıyorum çünkü bunu yapmadım ve güvenle hızlı bir şekilde örnek kod veremem.
Almo

Kullanın ListEğer ana iş parçacığı aramak istediğiniz işlevleri depolamak için Eyleminin. kilitlemek ve kopyalama Actionlisteyi Updatedaha sonra yürütmek özgün listesini temizlemek, geçici listeye fonksiyonu Actionşu şekilde kod Listana iş parçacığı üzerinde. Bunun nasıl yapılacağı hakkındaki diğer yazımdaki UnityThread bölümüne bakın . Örneğin, ana iş parçacığında bir işlevi çağırmak için, UnityThread.executeInUpdate(() => { transform.Rotate(new Vector3(0f, 90f, 0f)); });
Programcı,

Yanıtlar:


48

Güncelleme: 2018'de Unity, işi boşaltmanın ve çoklu CPU çekirdeğini kullanmanın bir yolu olarak bir C # İş Sistemini piyasaya sürüyor.

Aşağıdaki cevap bu sistemden önce gelir. Yine de işe yarayacak, ancak ihtiyaçlarınıza bağlı olarak modern Birlik'te daha iyi seçenekler mevcut olabilir. Özellikle, iş sistemi, aşağıda açıklanan, elle oluşturulan iş parçacıklarının güvenli bir şekilde erişebileceği konusundaki bazı kısıtlamaları gideriyor gibi görünmektedir. Örneğin, radikalleri gösteren ve paralel olarak kafesler oluşturan önizleme raporunu deneyen geliştiriciler .

Kullanıcıları bu iş sistemini kullanarak deneyim sahibi olanları, motorun mevcut durumunu yansıtan kendi cevaplarını eklemek için davet ediyorum.


Birlik'teki ağır görevler için iş parçacığını geçmişte kullandım (genellikle görüntü ve geometri işleme) ve iki uyarı ile diğer C # uygulamalarında iş parçacığı kullanmaktan çok farklı değil:

  1. Unity, biraz daha eski bir .NET alt kümesi kullandığından, kutudan kullanamayacağımız bazı yeni iş parçacığı özellikleri ve kitaplıkları vardır, ancak hepsi orada.

  2. Almo'nun yukarıdaki bir açıklamada not ettiği gibi, çoğu Unity türü güvenli değildir ve bunları ana ipten oluşturmaya, kullanmaya ve hatta karşılaştırmaya çalışırsanız istisnalar atar. Akılda tutulması gereken şeyler:

    • Yaygın bir durum, üyelerine erişmeye çalışmadan önce bir GameObject veya Monobehaviour referansının boş olup olmadığını kontrol etmektir. myUnityObject == nullUnityEngine.Object öğesinden çıkan herhangi bir şey için aşırı yüklenmiş bir işleci çağırır, ancak System.Object.ReferenceEquals()bunun etrafında bir dereceye kadar çalışır - yalnızca bir Destroy () ed GameObject öğesinin aşırı yüklemeyi null değerine eşit olarak karşılaştırdığını, ancak henüz null için ReferenceEqual olmadığını unutmayın.

    • Unity türlerinden parametreleri okumak genellikle başka bir iş parçacığında güvenlidir (yukarıdaki gibi boşları kontrol etmeye dikkat ettiğiniz sürece hemen bir istisna atmayacaktır), ancak Philipp'in burada ana iş parçacığının durumu değiştirdiği uyarısını not edin. sen okurken. Tutarsız durumları okumaktan kaçınmak için kimin neyi ne zaman değiştirebileceği konusunda disiplinli olmanız gerekir; bu durum, izleyebileceğimiz yirmi altı milisaniyelik sürelere bağlı olduklarından, izini sürdürebilmeleri zor olan hatalara yol açabilir. İsteğe bağlı olarak üremiyorum.

    • Rastgele ve Zaman statik üyeleri kullanılamaz. Rastgele olmanız gerekiyorsa, iş parçacığı başına bir System.Random örneği ve zamanlama bilgisine ihtiyacınız varsa System.Diagnostics.Stopwatch'ı oluşturun.

    • Mathf fonksiyonları, Vector, Matrix, Quaternion ve Color yapılarının tümü iş parçacığı boyunca iyi çalışır, böylece hesaplamalarınızın çoğunu ayrı ayrı yapabilirsiniz

    • GameObjects oluşturma, Monobehaviours ekleme veya Doku, Kafes, Malzeme vb. Oluşturma / güncelleme işlemlerinin hepsi ana iş parçacığında gerçekleşmelidir. Geçmişte bunlarla çalışmam gerektiğinde, işçi-iş parçacığımın ham verileri hazırladığı bir üretici-tüketici kuyruğu hazırladım (bir örgü veya dokuya uygulanacak büyük bir vektör / renk dizisi gibi), ve ana iş parçacığındaki bir Güncelleme veya Koroutin veriler için anket yapar ve uygular.

Bu notların dışında kalıyorum, işte genelde dişli işlerde kullandığım bir kalıp. Bunun en iyi uygulama şekli olduğunu garanti etmiyorum ama işi halleder. (Geliştirilmesi gereken yorumlar veya düzenlemeler memnuniyetle karşılanmaktadır - iş parçacığının yalnızca temelleri bildiğim çok derin bir konu olduğunu biliyorum)

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.
    }
}

Çalışmayı kesinlikle hız için dişler arasında bölmeniz gerekmiyorsa ve bunu engellememenin bir yolunu arıyorsanız, oyununuzun geri kalanı tıklamaya devam ediyor, Unity'deki daha hafif bir çözüm Coroutines . Bunlar, bazı işleri yapabilen işlevlerdir ve daha sonra ne yaptığını devam ettirmek ve daha sonra kesintisiz bir şekilde devam etmek için motora tekrar kontrol sağlar.

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...
        }
    }
}

Bu, herhangi bir özel temizlik hususuna ihtiyaç duymaz, çünkü motor (söyleyebileceğim kadarıyla), sizin için tahrip olmuş nesnelerden gelen korotenlerden kurtulur.

Yöntemin tüm yerel durumu, üretilip devam ettiğinde korunur; bu nedenle, birçok amaç, başka bir iş parçacığında kesintisiz çalışıyormuş gibi çalışır (ancak ana iş parçacığında çalışmanın tüm kolaylıklarına sahipsiniz). Her yinelemenin, ana ipliğinizi makul olmayan bir şekilde yavaşlatmayacak kadar kısa olduğundan emin olmanız gerekir.

Önemli işlemlerin bir verimle ayrılmadığından emin olarak, tek iş parçacıklı davranış tutarlılığı elde edebilirsiniz - ana iş parçasındaki başka hiçbir komut dosyasının veya sistemin üzerinde çalışmakta olduğunuz verileri değiştiremeyeceğini bilmek.

Verim dönüş hattı size birkaç seçenek sunar. Yapabilirsin...

  • yield return null Bir sonraki karenin Güncellemesinden sonra devam etmek ()
  • yield return new WaitForFixedUpdate() bir sonraki FixedUpdate () işleminden sonra devam etmek için
  • yield return new WaitForSeconds(delay) belirli bir oyun süresi geçtikten sonra devam etmek
  • yield return new WaitForEndOfFrame() GUI oluşturma işlemini tamamladıktan sonra devam etmek için
  • yield return myRequestburada myRequestbir WWW örneği, istenen veri web veya diskten yüklenmeyi tamamladıktan sonra devam etmek için.
  • yield return otherCoroutinenerede otherCoroutinebir olduğunu eşyordam örneği sonra devam edilecek, otherCoroutinetamamlanıncaya. Bu genellikle, yield return StartCoroutine(OtherCoroutineMethod())yürütme zincirinin, istediği zaman üretebileceği yeni bir korotine zincirlenmesi için kullanılır.

    • Deneysel olarak, ikinci atlamayı atlamak ve aynı bağlamda zincirleme yürütmek istiyorsanız, StartCoroutinebasitçe yazma yield return OtherCoroutineMethod()aynı amacı gerçekleştirir.

      Bir iç tamamlayan StartCoroutineikinci bir nesne ile birlikte iç içe eşyordam çalıştırmak istiyorsanız hala gibi yararlı olabiliryield return otherObject.StartCoroutine(OtherObjectsCoroutineMethod())

... coroutinin bir sonraki dönüşünü ne zaman yapmasını istediğine bağlı olarak.

Veya yield break;korotinin sonuna gelmeden önce, return;geleneksel bir yönteme erken gitmek için kullanabileceğiniz şekilde durdurmak için .


Korotinleri yalnızca yineleme 20 ms'den uzun sürdükten sonra üretmek için kullanmanın bir yolu var mı?
DarkDestry

@DarkDestry İç çevrinizde ne kadar zaman harcadığınızı zamanlamak için bir kronometre örneği kullanabilir ve kronometreyi sıfırlamadan ve iç çevrime devam etmeden önce, ürettiğiniz bir dış çevreye geçebilirsiniz.
DMGregory

1
İyi cevap! Sadece coroutines kullanmak için bir neden daha eklemek istiyorum eğer webgl için inşa etmek gerekirse, iş parçacığı kullanmazsanız işe yarayacak olan çalışır. Şu anda büyük bir projede şu baş ağrısıyla oturmak: P
Mikael Högström

İyi cevap ve teşekkürler. Sadece, ipliği defalarca durdurup yeniden başlatmam gerekip gerekmediğini merak ediyorum, iptal edip başlamayı deniyorum,
hatam

@flankechen İplik çalıştırma ve sökme işlemi nispeten pahalı işlemlerdir, bu nedenle sık sık ipliğin hazır bulundurulmasını tercih ederiz, ancak bunun için taze işler yaptığımızda sinyal vermek için semaforlar veya monitörler gibi şeyler kullanılır. Kullanım durumunuzu detaylı bir şekilde anlatan yeni bir soru göndermek ister misiniz? İnsanlar bunu başarmak için etkili yollar önerebilir mi?
DMGregory

0

Ağır hesaplamanızı başka bir başlığa koyabilirsiniz, ancak Birliğin API'si iş parçacığı güvenli değil, bunları ana iş parçacığında yürütmeniz gerekir.

Pekala, bu paketi Asset Store'da deneyebilirsiniz, iş parçacığını daha kolay kullanmanıza yardımcı olur. http://u3d.as/wQg Bir iş parçacığı başlatmak ve Unity API'yı güvenli bir şekilde yürütmek için yalnızca bir satır kod kullanabilirsiniz.



0

@DMGregory bunu gerçekten iyi açıkladı.

Hem diş açma hem de koroinler kullanılabilir. Ana iş parçacığını boşaltmak için eski ve daha sonra ana iş parçacığı denetimi döndürmek için eski. İpliğin ayrılması için ağır kaldırmayı itmek daha makul görünür. Bu nedenle, iş kuyrukları peşinde olduğunuz şey olabilir.

Unity Wiki'de gerçekten iyi JobQueue örnek yazısı var.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.