एकता में मुख्य सूत्र को कैसे फ्रीज नहीं किया जाए?


33

मेरे पास एक स्तरीय पीढ़ी का एल्गोरिथ्म है जो कम्प्यूटेशनल रूप से भारी है। जैसे, इसे कॉल करने से हमेशा गेम स्क्रीन फ्रीज हो जाता है। गेम को जमे हुए नहीं होने का संकेत देने के लिए मैं एक दूसरे थ्रेड पर फ़ंक्शन को कैसे रख सकता हूं जबकि गेम अभी भी लोडिंग स्क्रीन को प्रस्तुत करना जारी रखता है?


1
आप पृष्ठभूमि में अन्य कार्य चलाने के लिए थ्रेडिंग सिस्टम का उपयोग कर सकते हैं ... लेकिन वे एकता एपीआई में कुछ भी नहीं कह सकते हैं क्योंकि यह सामान थ्रेडसेफ़ नहीं है। सिर्फ इसलिए कमेंट कर रहा हूं क्योंकि मैंने ऐसा नहीं किया है और आप आत्मविश्वास से जल्दी कोड उदाहरण नहीं दे सकते।
आलमो

Listउन कार्यों को संग्रहीत करने के लिए क्रियाओं का उपयोग जिन्हें आप मुख्य थ्रेड में कॉल करना चाहते हैं। फ़ंक्शन Actionमें सूची Updateको अस्थायी सूची में लॉक और कॉपी करें , मूल सूची को साफ़ करें फिर मुख्य थ्रेड Actionमें उस कोड को निष्पादित करें List। यह करने के तरीके पर मेरे अन्य पोस्ट से UnityThread देखें । उदाहरण के लिए, मुख्य थ्रेड पर एक फ़ंक्शन को कॉल करने के लिए, UnityThread.executeInUpdate(() => { transform.Rotate(new Vector3(0f, 90f, 0f)); });
प्रोग्रामर

जवाबों:


48

अपडेट: २०१, में, एकता एक सी # जॉब सिस्टम को काम करने और कई सीपीयू कोर का उपयोग करने के तरीके के रूप में तैयार कर रही है।

नीचे दिया गया उत्तर इस प्रणाली को पूर्व निर्धारित करता है। यह अभी भी काम करेगा, लेकिन आपकी आवश्यकताओं के आधार पर, आधुनिक यूनिटी में बेहतर विकल्प उपलब्ध हो सकते हैं। विशेष रूप से, नौकरी प्रणाली कुछ सीमाओं को हल करने के लिए प्रकट होती है जो मैन्युअल रूप से निर्मित थ्रेड्स सुरक्षित रूप से उपयोग कर सकते हैं, नीचे वर्णित है। उदाहरण के लिए, रेकास्ट्स का प्रदर्शन और समानांतर में मेषों का निर्माण करते हुए पूर्वावलोकन रिपोर्ट के साथ प्रयोग करने वाले डेवलपर्स

मैं इस नौकरी प्रणाली का उपयोग करने के लिए अपने स्वयं के उत्तर जोड़ने के लिए अनुभव के साथ उपयोगकर्ताओं को आमंत्रित करता हूं जो इंजन की वर्तमान स्थिति को दर्शाता है।


मैंने अतीत में हैवीवेट कार्यों के लिए थ्रेडिंग का उपयोग किया है (आमतौर पर छवि और ज्यामिति प्रसंस्करण), और यह दो सीट्स के साथ अन्य सी # अनुप्रयोगों में थ्रेड का उपयोग करने की तुलना में बहुत भिन्न नहीं है:

  1. क्योंकि एकता .NET के कुछ पुराने उपसमुच्चय का उपयोग करती है, इसलिए कुछ नई थ्रेडिंग सुविधाएँ और लाइब्रेरीज़ हैं जिनका उपयोग हम बॉक्स से बाहर नहीं कर सकते हैं, लेकिन मूल बातें सभी हैं।

  2. जैसा कि अल्मो ने ऊपर एक टिप्पणी में लिखा है, कई एकता प्रकार थ्रेडसेफ़ नहीं हैं, और अपवादों को फेंक देंगे यदि आप मुख्य धागे से निर्माण, उपयोग, या यहां तक ​​कि उनकी तुलना करने की कोशिश करते हैं। ध्यान रखने योग्य बातें:

    • एक सामान्य मामला यह देखने के लिए जाँच कर रहा है कि क्या एक GameObject या Monobehaviour संदर्भ अपने सदस्यों तक पहुँचने की कोशिश करने से पहले अशक्त है। myUnityObject == nullUnityEngine.Object से उतरे किसी भी चीज के लिए एक ओवरलोडेड ऑपरेटर को कॉल System.Object.ReferenceEquals()करता है , लेकिन यह एक डिग्री के आसपास काम करता है - बस याद रखें कि एक नष्ट () एड GameObject ओवरलोड का उपयोग कर के बराबर की तुलना करता है, लेकिन अभी तक नहीं है।

    • एकता के प्रकारों से पठन पैरामीटर आमतौर पर किसी अन्य थ्रेड पर सुरक्षित होता है (इसमें वह तुरंत एक अपवाद नहीं फेंकता है जब तक आप ऊपर के रूप में नल की जांच करने के लिए सावधान रहें), लेकिन यहां फिलिप की चेतावनी पर ध्यान दें कि मुख्य धागा राज्य को संशोधित कर सकता है जब आप इसे पढ़ रहे हों। आपको इस बारे में अनुशासित रहने की आवश्यकता है कि कुछ असंगत स्थिति को पढ़ने से बचने के लिए किसने और कब संशोधित करने की अनुमति दी है, जिससे ऐसे कीड़े पैदा हो सकते हैं, जिन्हें ट्रैक करना मुश्किल हो सकता है क्योंकि वे धागे के बीच उप-मिलीसेकंड समय पर निर्भर होते हैं जो हम कर सकते हैं इच्छा पर पुन: पेश नहीं किया जाएगा।

    • रैंडम और टाइम स्टैटिक सदस्य उपलब्ध नहीं हैं। अगर आपको यादृच्छिकता की आवश्यकता है, तो System.Random प्रति थ्रेड का एक उदाहरण बनाएं और यदि आपको समय की जानकारी चाहिए, तो System.Diagnostics.Stopwatch।

    • मैथफ़ फ़ंक्शंस, वेक्टर, मैट्रिक्स, क्वाटरनियन, और रंग संरचनाएं सभी थ्रेड्स में ठीक काम करती हैं, इसलिए आप अपनी अधिकांश गणना अलग-अलग कर सकते हैं

    • GameObjects बनाना, Monobehaviours को अटैच करना, या टेक्सचर, मेश, मैटेरियल्स आदि को बनाना / बनाना सभी मुख्य धागे पर होने चाहिए। अतीत में जब मुझे इन के साथ काम करने की आवश्यकता होती है, तो मैंने एक निर्माता-उपभोक्ता कतार लगाई है, जहाँ मेरा कर्मचारी धागा कच्चा डेटा तैयार करता है (जैसे जाल या बनावट पर लागू करने के लिए वैक्टर / रंगों का एक बड़ा सरणी), और डेटा के लिए मुख्य थ्रेड पोल्स पर एक अपडेट या कोरटाइन और इसे लागू करता है।

उन नोटों के रास्ते से बाहर, यहाँ एक पैटर्न है जिसे मैं अक्सर थ्रेडेड वर्क के लिए उपयोग करता हूँ। मैं इस बात की कोई गारंटी नहीं देता कि यह एक सर्वोत्तम अभ्यास शैली है, लेकिन यह काम पूरा करती है। (टिप्पणी या संपादन में सुधार का स्वागत है - मुझे पता है कि थ्रेडिंग एक बहुत गहरा विषय है, जिसके बारे में मैं केवल मूल बातें जानता हूं)

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

यदि आपको गति के लिए थ्रेड्स में कार्य को विभाजित करने की कड़ाई से आवश्यकता नहीं है, और आप इसे गैर-अवरोधक बनाने का एक तरीका ढूंढ रहे हैं, तो आपके बाकी गेम टिक जाते हैं, एकता में एक हल्का-वजन समाधान है Coroutines । ये ऐसे कार्य हैं जो कुछ काम कर सकते हैं फिर इंजन पर नियंत्रण बनाए रख सकते हैं ताकि यह सुनिश्चित हो सके कि यह क्या कर रहा है, और बाद के समय में इसे फिर से शुरू करें।

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

इसके लिए किसी विशेष सफाई के विचार की आवश्यकता नहीं है, क्योंकि इंजन (अब तक मैं बता सकता हूं) आपके लिए नष्ट हो चुकी वस्तुओं से कोरटाइन से छुटकारा दिलाता है।

जब यह उपज और फिर से शुरू होता है, तो विधि की सभी स्थानीय स्थिति को संरक्षित किया जाता है, इसलिए कई उद्देश्यों के लिए यह ऐसा है जैसे यह किसी अन्य धागे पर निर्बाध रूप से चल रहा था (लेकिन आपके पास मुख्य धागे पर चलने की सभी उपयुक्तताएं हैं)। आपको बस यह सुनिश्चित करने की आवश्यकता है कि इसका प्रत्येक पुनरावृत्ति इतनी कम है कि यह आपके मुख्य धागे को अनुचित रूप से धीमा नहीं करने वाला है।

यह सुनिश्चित करने से कि महत्वपूर्ण संचालन को एक उपज द्वारा अलग नहीं किया जाता है, आप एकल-थ्रेडेड व्यवहार की स्थिरता प्राप्त कर सकते हैं - यह जानकर कि मुख्य थ्रेड पर कोई अन्य स्क्रिप्ट या सिस्टम आपके द्वारा काम करने के बीच में डेटा को संशोधित नहीं कर सकता है।

यील्ड रिटर्न लाइन आपको कुछ विकल्प देता है। आप ऐसा कर सकते हैं...

  • yield return null अगले फ्रेम के अपडेट के बाद फिर से शुरू करें ()
  • yield return new WaitForFixedUpdate() अगले फिक्स्डडेट () के बाद फिर से शुरू करें
  • yield return new WaitForSeconds(delay) खेल समय की एक निश्चित राशि के बाद फिर से शुरू करने के लिए
  • yield return new WaitForEndOfFrame() जीयूआई के प्रतिपादन के बाद फिर से शुरू करना
  • yield return myRequestmyRequestएक WWW उदाहरण है, जहां एक बार अनुरोधित डेटा को वेब या डिस्क से लोड करना समाप्त करना है।
  • yield return otherCoroutineजहां otherCoroutineएक कोराटाइन उदाहरण है , otherCoroutineपूरा होने के बाद फिर से शुरू करने के लिए । यह अक्सर yield return StartCoroutine(OtherCoroutineMethod())एक नए कोरटाइन को श्रृंखला निष्पादन के रूप में उपयोग किया जाता है जो कि जब चाहे तब खुद को उपज सकता है।

    • प्रयोगात्मक रूप से, दूसरे को छोड़ देना StartCoroutineऔर केवल लिखना yield return OtherCoroutineMethod()उसी लक्ष्य को पूरा करता है यदि आप उसी संदर्भ में श्रृंखला निष्पादन चाहते हैं।

      StartCoroutineयदि आप नेस्टेड कॉरआउट को दूसरी वस्तु के साथ चलाना चाहते हैं, तो अंदर लपेटना अभी भी उपयोगी हो सकता हैyield return otherObject.StartCoroutine(OtherObjectsCoroutineMethod())

... इसके आधार पर जब आप चाहते हैं कि कोरटाइन अपना अगला मोड़ ले।

या yield break;अंत तक पहुंचने से पहले कोरोटीन को रोकने के लिए, जिस तरह से आप return;एक पारंपरिक विधि के शुरुआती-आउट का उपयोग कर सकते हैं ।


क्या पुनरावृत्ति 20ms से अधिक होने के बाद ही पैदावार का उपयोग करने का एक तरीका है?
डार्कडेस्ट्री

@DarkDestry आप स्टॉपवॉच उदाहरण का उपयोग समय के लिए कर सकते हैं कि आप अपने आंतरिक लूप में कितना समय बिता रहे हैं, और एक बाहरी लूप को तोड़ें जहां आप स्टॉपवॉच को रीसेट करने और इनर लूप को फिर से शुरू करने से पहले उपजते हैं।
DMGregory

1
अच्छा उत्तर! मैं केवल यह जोड़ना चाहता हूं कि कॉरटाइन्स का उपयोग करने का एक और कारण यह है कि यदि आपको कभी भी वेबलॉग के लिए निर्माण करने की आवश्यकता होती है तो यह काम करेगा जो कि थ्रेड्स का उपयोग करने पर ऐसा नहीं होगा। अभी एक विशाल परियोजना में उस सिरदर्द के साथ बैठे: पी
मिकेल होर्गस्ट्रॉम

अच्छा जवाब और धन्यवाद। बस सोच रहा था कि क्या होगा अगर मुझे कई बार थ्रेड को रोकने और पुनरारंभ करने की आवश्यकता होती है, मैं गर्भपात की कोशिश करता हूं और शुरू करता हूं, मुझे त्रुटि मिलती है
flankechen

@flankechen थ्रेड स्टार्ट-अप और टियरडाउन अपेक्षाकृत महंगे ऑपरेशन हैं, इसलिए अक्सर हम थ्रेड को उपलब्ध रखने के लिए पसंद करेंगे, लेकिन डॉर्मेंट - सेमाफोरेस या मॉनिटर जैसी चीजों का उपयोग करके जब हम इसके लिए नए काम करते हैं तो संकेत देते हैं। अपने उपयोग के मामले का विस्तार से वर्णन करते हुए एक नया प्रश्न पोस्ट करना चाहते हैं, और लोग इसे प्राप्त करने के लिए कुशल तरीके सुझा सकते हैं?
DMGregory

0

आप अपनी भारी गणना को दूसरे धागे में डाल सकते हैं, लेकिन एकता का एपीआई सुरक्षित नहीं है, आपको उन्हें मुख्य धागे में निष्पादित करना होगा।

वैसे आप एसेट स्टोर पर इस पैकेज को आज़मा सकते हैं जो आपको थ्रेडिंग का आसान उपयोग करने में मदद करेगा। http://u3d.as/wQg आप केवल एक धागा शुरू करने और एकता एपीआई को सुरक्षित रूप से निष्पादित करने के लिए केवल एक पंक्ति की कोड का उपयोग कर सकते हैं।



0

@DMGregory ने इसे वास्तव में अच्छी तरह समझाया।

थ्रेडिंग और कोरटाइन दोनों का उपयोग किया जा सकता है। मुख्य धागे को उतारने के लिए पूर्व और बाद में मुख्य धागे पर नियंत्रण वापस करने के लिए। थ्रेड को जब्त करने के लिए भारी उठाने को अधिक उचित लगता है। इसलिए, नौकरी की कतारें वही हो सकती हैं जो आप के बाद हैं।

यूनिटी विकी पर वास्तव में अच्छा JobQueue उदाहरण स्क्रिप्ट है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.