2016-12-26 7 views
6

में फ़ंक्शन को कॉल करें मेरी समस्या यह है कि मैं कुछ लागू करने के लिए यूनिटी सॉकेट का उपयोग करने का प्रयास करता हूं। प्रत्येक बार, जब मुझे एक नया संदेश मिलता है तो मुझे इसे अपडेटैटटेक्स्ट में अपडेट करना होगा (यह एकता टेक्स्ट है)। हालांकि, जब मैं निम्नलिखित कोड करता हूं, तो शून्य अद्यतन हर बार कॉल नहीं करता है।किसी अन्य थ्रेड से यूनिटी एपीआई का उपयोग करें या मुख्य थ्रेड

कारण मैं शून्य getInformation में updatetext.GetComponent<Text>().text = "From server: "+tempMesg; शामिल नहीं है के लिए इस समारोह, सूत्र में है जब मैं शामिल है कि getInformation() में यह एक त्रुटि के साथ आ जाएगा:

getcomponentfastpath can only be called from the main thread

मैं इस समस्या लगता है क्या मुझे नहीं पता कि मुख्य धागे और बच्चे धागे को सी # में कैसे चलाया जाए?

using UnityEngine; 
using System.Collections; 
using System; 
using System.Net.Sockets; 
using System.Text; 
using System.Threading; 
using UnityEngine.UI; 


public class Client : MonoBehaviour { 

    System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient(); 
    private Thread oThread; 

// for UI update 
    public GameObject updatetext; 
    String tempMesg = "Waiting..."; 

    // Use this for initialization 
    void Start() { 
     updatetext.GetComponent<Text>().text = "Waiting..."; 
     clientSocket.Connect("10.132.198.29", 8888); 
     oThread = new Thread (new ThreadStart (getInformation)); 
     oThread.Start(); 
     Debug.Log ("Running the client"); 
    } 

    // Update is called once per frame 
    void Update() { 
     updatetext.GetComponent<Text>().text = "From server: "+tempMesg; 
     Debug.Log (tempMesg); 
    } 

    void getInformation(){ 
     while (true) { 
      try { 
       NetworkStream networkStream = clientSocket.GetStream(); 
       byte[] bytesFrom = new byte[10025]; 
       networkStream.Read (bytesFrom, 0, (int)bytesFrom.Length); 
       string dataFromClient = System.Text.Encoding.ASCII.GetString (bytesFrom); 
       dataFromClient = dataFromClient.Substring (0, dataFromClient.IndexOf ("$")); 
       Debug.Log (" >> Data from Server - " + dataFromClient); 

       tempMesg = dataFromClient; 

       string serverResponse = "Last Message from Server" + dataFromClient; 

       Byte[] sendBytes = Encoding.ASCII.GetBytes (serverResponse); 
       networkStream.Write (sendBytes, 0, sendBytes.Length); 
       networkStream.Flush(); 
       Debug.Log (" >> " + serverResponse); 

      } catch (Exception ex) { 
       Debug.Log ("Exception error:" + ex.ToString()); 
       oThread.Abort(); 
       oThread.Join(); 
      } 
//   Thread.Sleep (500); 
     } 
    } 
} 

उत्तर

0

कि अपवाद के लिए कारण यह है कि UnityEngine सुरक्षित थ्रेड नहीं है: या वहाँ शायद अन्य समस्याओं ... आशा किसी की मदद कर सकते हैं .. मेरी कोड नहीं है। थ्रेड के बजाय coroutine का उपयोग करें:

void Start() { 
    updatetext.GetComponent<Text>().text = "Waiting..."; 
    clientSocket.Connect("10.132.198.29", 8888); 
    StartCoroutine(getInformation()); 
    Debug.Log ("Running the client"); 
} 

// Update is called once per frame 
void Update() { 
    updatetext.GetComponent<Text>().text = "From server: "+tempMesg; 
    Debug.Log (tempMesg); 
} 

IEnumerator getInformation(){ 
    while (true) { 
     //... 
     yield return null; 
     if(done) break;//terminate condition 
    } 
} 

//... से पहले अपने कोड डाल दिया और संकेत मिलता है जब जानकारी के लिए तैयार है और विधि को समाप्त करना चाहिए एक बूलियन done निर्धारित किया है।

+0

हाय बिजान, सामान्य यूनिटी बनाम कोरआउटिन फ़ंक्शन में नेटवर्क फ़ंक्शन को कॉल करने के बीच कोई अंतर नहीं है। ओपी में अभी भी [फ्रीजिंग] होगा (http://stackoverflow.com/q/41207487/3785314) समस्याएं अगर थ्रेड या एसिंक का उपयोग नहीं किया जाता है। मैं इसे एक उत्तर में समझाऊंगा। – Programmer

+0

आप यह देखने के लिए मेरा जवाब देख सकते हैं कि मैं किस बारे में बात कर रहा हूं। किसी सर्वर से कनेक्ट या प्राप्त करते समय कोरआउटिन का उपयोग करना अभी भी ऐप को फ्रीज करेगा। आप केवल कोरआउट के साथ नेटवर्क सामान का उपयोग करते हैं यदि यह एक यूनिटी एपीआई है जैसे 'डब्ल्यूडब्ल्यूडब्लू' या 'यूनिटीवेबआरक्वेट' जो कोरआउट उपज का समर्थन करता है। सी # सॉकेट नहीं करता है। आपको 'थ्रेड' या' Async 'फ़ंक्शंस जैसे' BeginAccept' और 'BeginReceive'' का उपयोग करना होगा। – Programmer

+1

@ प्रोग्रामर यह मूल्यवान जानकारी है। धन्यवाद – Bijan

24

एकता सुरक्षित नहीं Thread है, तो वे यह असंभव है, जब इसके एपीआई एक और Thread से प्रयोग किया जाता है एक अपवाद फेंकने के लिए एक तंत्र जोड़कर एक और Thread से उनके एपीआई कॉल करने के लिए बनाने का फैसला किया।

इस प्रश्न से कई बार पूछा गया है, लेकिन उनमें से किसी के लिए कोई उचित समाधान/उत्तर नहीं दिया गया है। उत्तर आमतौर पर "प्लगइन का उपयोग करते हैं" या कुछ थ्रेड-सुरक्षित नहीं करते हैं। उम्मीद है कि यह आखिरी होगा।

समाधान जो आप आमतौर पर स्टैक ओवरफ्लो या यूनिटी की फोरम वेबसाइट पर देखते हैं, वह केवल boolean वैरिएबल का उपयोग करने के लिए मुख्य थ्रेड को यह जानने के लिए है कि आपको मुख्य Thread में कोड निष्पादित करने की आवश्यकता है। यह सही नहीं है क्योंकि यह थ्रेड-सुरक्षित नहीं है और आपको कॉल करने के लिए कौन सा फ़ंक्शन प्रदान करने के लिए नियंत्रण नहीं देता है। क्या होगा यदि आपके पास एकाधिक Threads हैं जो मुख्य धागे को सूचित करने की आवश्यकता है?

एक और समाधान जो आप देखेंगे Thread के बजाय कोरआउट का उपयोग करना है। यह काम नहीं करता है। सॉकेट के लिए कोरआउट का उपयोग कुछ भी नहीं बदलेगा। आप अभी भी अपनी freezing समस्याओं के साथ समाप्त हो जाएंगे। आपको अपने Thread कोड से चिपके रहना होगा या Async का उपयोग करना होगा।

ऐसा करने के उचित तरीकों में से एक है List जैसे संग्रह बनाना। जब आपको मुख्य थ्रेड में निष्पादित करने की आवश्यकता होती है, तो उस फ़ंक्शन को कॉल करें जो कोड को Action में निष्पादित करने के लिए संग्रहीत करता है। कॉपी है कि एक स्थानीय Action की List को Action की List तो उस List तो उस List स्पष्ट में स्थानीय Action से कोड निष्पादित। यह अन्य Threads को निष्पादित करने के लिए प्रतीक्षा करने से रोकता है।

फ़ंक्शन को सूचित करने के लिए आपको volatile boolean जोड़ने की भी आवश्यकता है कि List निष्पादित करने के लिए कोड प्रतीक्षा कर रहा है।List को स्थानीय List पर प्रतिलिपि करते समय, इसे lock कीवर्ड के चारों ओर लपेटने के लिए इसे किसी अन्य थ्रेड को लिखने से रोकने के लिए लपेटा जाना चाहिए।

एक स्क्रिप्ट करता है कि क्या मैं उपर्युक्त:

UnityThread स्क्रिप्ट:

#define ENABLE_UPDATE_FUNCTION_CALLBACK 
#define ENABLE_LATEUPDATE_FUNCTION_CALLBACK 
#define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK 

using System; 
using System.Collections; 
using UnityEngine; 
using System.Collections.Generic; 


public class UnityThread : MonoBehaviour 
{ 
    //our (singleton) instance 
    private static UnityThread instance = null; 


    ////////////////////////////////////////////////UPDATE IMPL//////////////////////////////////////////////////////// 
    //Holds actions received from another Thread. Will be coped to actionCopiedQueueUpdateFunc then executed from there 
    private static List<System.Action> actionQueuesUpdateFunc = new List<Action>(); 

    //holds Actions copied from actionQueuesUpdateFunc to be executed 
    List<System.Action> actionCopiedQueueUpdateFunc = new List<System.Action>(); 

    // Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame 
    private volatile static bool noActionQueueToExecuteUpdateFunc = true; 


    ////////////////////////////////////////////////LATEUPDATE IMPL//////////////////////////////////////////////////////// 
    //Holds actions received from another Thread. Will be coped to actionCopiedQueueLateUpdateFunc then executed from there 
    private static List<System.Action> actionQueuesLateUpdateFunc = new List<Action>(); 

    //holds Actions copied from actionQueuesLateUpdateFunc to be executed 
    List<System.Action> actionCopiedQueueLateUpdateFunc = new List<System.Action>(); 

    // Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame 
    private volatile static bool noActionQueueToExecuteLateUpdateFunc = true; 



    ////////////////////////////////////////////////FIXEDUPDATE IMPL//////////////////////////////////////////////////////// 
    //Holds actions received from another Thread. Will be coped to actionCopiedQueueFixedUpdateFunc then executed from there 
    private static List<System.Action> actionQueuesFixedUpdateFunc = new List<Action>(); 

    //holds Actions copied from actionQueuesFixedUpdateFunc to be executed 
    List<System.Action> actionCopiedQueueFixedUpdateFunc = new List<System.Action>(); 

    // Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame 
    private volatile static bool noActionQueueToExecuteFixedUpdateFunc = true; 


    //Used to initialize UnityThread. Call once before any function here 
    public static void initUnityThread(bool visible = false) 
    { 
     if (instance != null) 
     { 
      return; 
     } 

     if (Application.isPlaying) 
     { 
      // add an invisible game object to the scene 
      GameObject obj = new GameObject("MainThreadExecuter"); 
      if (!visible) 
      { 
       obj.hideFlags = HideFlags.HideAndDontSave; 
      } 

      DontDestroyOnLoad(obj); 
      instance = obj.AddComponent<UnityThread>(); 
     } 
    } 

    public void Awake() 
    { 
     DontDestroyOnLoad(gameObject); 
    } 

    //////////////////////////////////////////////COROUTINE IMPL////////////////////////////////////////////////////// 
#if (ENABLE_UPDATE_FUNCTION_CALLBACK) 
    public static void executeCoroutine(IEnumerator action) 
    { 
     if (instance != null) 
     { 
      executeInUpdate(() => instance.StartCoroutine(action)); 
     } 
    } 

    ////////////////////////////////////////////UPDATE IMPL//////////////////////////////////////////////////// 
    public static void executeInUpdate(System.Action action) 
    { 
     if (action == null) 
     { 
      throw new ArgumentNullException("action"); 
     } 

     lock (actionQueuesUpdateFunc) 
     { 
      actionQueuesUpdateFunc.Add(action); 
      noActionQueueToExecuteUpdateFunc = false; 
     } 
    } 

    public void Update() 
    { 
     if (noActionQueueToExecuteUpdateFunc) 
     { 
      return; 
     } 

     //Clear the old actions from the actionCopiedQueueUpdateFunc queue 
     actionCopiedQueueUpdateFunc.Clear(); 
     lock (actionQueuesUpdateFunc) 
     { 
      //Copy actionQueuesUpdateFunc to the actionCopiedQueueUpdateFunc variable 
      actionCopiedQueueUpdateFunc.AddRange(actionQueuesUpdateFunc); 
      //Now clear the actionQueuesUpdateFunc since we've done copying it 
      actionQueuesUpdateFunc.Clear(); 
      noActionQueueToExecuteUpdateFunc = true; 
     } 

     // Loop and execute the functions from the actionCopiedQueueUpdateFunc 
     for (int i = 0; i < actionCopiedQueueUpdateFunc.Count; i++) 
     { 
      actionCopiedQueueUpdateFunc[i].Invoke(); 
     } 
    } 
#endif 

    ////////////////////////////////////////////LATEUPDATE IMPL//////////////////////////////////////////////////// 
#if (ENABLE_LATEUPDATE_FUNCTION_CALLBACK) 
    public static void executeInLateUpdate(System.Action action) 
    { 
     if (action == null) 
     { 
      throw new ArgumentNullException("action"); 
     } 

     lock (actionQueuesLateUpdateFunc) 
     { 
      actionQueuesLateUpdateFunc.Add(action); 
      noActionQueueToExecuteLateUpdateFunc = false; 
     } 
    } 


    public void LateUpdate() 
    { 
     if (noActionQueueToExecuteLateUpdateFunc) 
     { 
      return; 
     } 

     //Clear the old actions from the actionCopiedQueueLateUpdateFunc queue 
     actionCopiedQueueLateUpdateFunc.Clear(); 
     lock (actionQueuesLateUpdateFunc) 
     { 
      //Copy actionQueuesLateUpdateFunc to the actionCopiedQueueLateUpdateFunc variable 
      actionCopiedQueueLateUpdateFunc.AddRange(actionQueuesLateUpdateFunc); 
      //Now clear the actionQueuesLateUpdateFunc since we've done copying it 
      actionQueuesLateUpdateFunc.Clear(); 
      noActionQueueToExecuteLateUpdateFunc = true; 
     } 

     // Loop and execute the functions from the actionCopiedQueueLateUpdateFunc 
     for (int i = 0; i < actionCopiedQueueLateUpdateFunc.Count; i++) 
     { 
      actionCopiedQueueLateUpdateFunc[i].Invoke(); 
     } 
    } 
#endif 

    ////////////////////////////////////////////FIXEDUPDATE IMPL////////////////////////////////////////////////// 
#if (ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK) 
    public static void executeInFixedUpdate(System.Action action) 
    { 
     if (action == null) 
     { 
      throw new ArgumentNullException("action"); 
     } 

     lock (actionQueuesFixedUpdateFunc) 
     { 
      actionQueuesFixedUpdateFunc.Add(action); 
      noActionQueueToExecuteFixedUpdateFunc = false; 
     } 
    } 

    public void FixedUpdate() 
    { 
     if (noActionQueueToExecuteFixedUpdateFunc) 
     { 
      return; 
     } 

     //Clear the old actions from the actionCopiedQueueFixedUpdateFunc queue 
     actionCopiedQueueFixedUpdateFunc.Clear(); 
     lock (actionQueuesFixedUpdateFunc) 
     { 
      //Copy actionQueuesFixedUpdateFunc to the actionCopiedQueueFixedUpdateFunc variable 
      actionCopiedQueueFixedUpdateFunc.AddRange(actionQueuesFixedUpdateFunc); 
      //Now clear the actionQueuesFixedUpdateFunc since we've done copying it 
      actionQueuesFixedUpdateFunc.Clear(); 
      noActionQueueToExecuteFixedUpdateFunc = true; 
     } 

     // Loop and execute the functions from the actionCopiedQueueFixedUpdateFunc 
     for (int i = 0; i < actionCopiedQueueFixedUpdateFunc.Count; i++) 
     { 
      actionCopiedQueueFixedUpdateFunc[i].Invoke(); 
     } 
    } 
#endif 

    public void OnDisable() 
    { 
     if (instance == this) 
     { 
      instance = null; 
     } 
    } 
} 

उपयोग:

इस कार्यान्वयन आप में कार्यों कॉल करने के लिए अनुमति देता है सबसे प्रयुक्त एकता कार्य: Update, LateUpdate ए nd FixedUpdate फ़ंक्शंस। यह आपको मुख्य Thread में एक कोरआउट कार्य चलाने की अनुमति देता है। इसे अन्य यूनिटी कॉलबैक फ़ंक्शंस जैसे OnPreRender और OnPostRender में फ़ंक्शंस कॉल करने में सक्षम होने के लिए बढ़ाया जा सकता है।

। सबसे पहले, इसे Awake() फ़ंक्शन से प्रारंभ करें।

void Awake() 
{ 
    UnityThread.initUnityThread(); 
} 

एक और धागा से मुख्य Thread में एक कोड निष्पादित करें.अभियान:

UnityThread.executeInUpdate(() => 
{ 
    transform.Rotate(new Vector3(0f, 90f, 0f)); 
}); 

यह 90 डिग्री करने के लिए वर्तमान वस्तु scipt से जुड़ा हुआ है, रोटेट किए जाएंगे। अब आप एक और Thread में यूनिटी एपीआई (transform.Rotate) का उपयोग कर सकते हैं।

एक और धागा से मुख्य Thread में एक समारोह फोन करें.अभियान: 2 और # 3 नमूने Update समारोह में कार्यान्वित

Action rot = Rotate; 
UnityThread.executeInUpdate(rot); 


void Rotate() 
{ 
    transform.Rotate(new Vector3(0f, 90f, 0f)); 
} 

#।

एक और धागा से LateUpdate समारोह में एक कोड निष्पादित करें.अभियान:

इस का उदाहरण है एक कैमरा ट्रैकिंग कोड है। जब भौतिकी सामान कर इस तरह के Rigidbody करने के लिए बल के रूप में जोड़ने

इस का उदाहरण:

UnityThread.executeInLateUpdate(()=> 
{ 
    //Your code camera moving code 
}); 

एक और धागा से FixedUpdate समारोह में एक कोड निष्पादित करें.अभियान।

UnityThread.executeInFixedUpdate(()=> 
{ 
    //Your code physics code 
}); 

।एक और धागा से मुख्य Thread में एक coroutine समारोह शुरू करने के लिए: अंत में

UnityThread.executeCoroutine(myCoroutine()); 

IEnumerator myCoroutine() 
{ 
    Debug.Log("Hello"); 
    yield return new WaitForSeconds(2f); 
    Debug.Log("Test"); 
} 

, यदि आप LateUpdate और FixedUpdate कार्यों में कुछ भी निष्पादित करने के लिए की जरूरत नहीं है, आप नीचे दिए गए इस कोड की दोनों पंक्तियों टिप्पणी करना चाहिए:

//#define ENABLE_LATEUPDATE_FUNCTION_CALLBACK 
//#define ENABLE_FIXEDUPDATE_FUNCTION_CALLBACK 

इससे प्रदर्शन में वृद्धि होगी।

+0

क्षमा करें ... मैं आपके समाधान को लागू करने का प्रयास करता हूं ... लेकिन जब मैं UnityThread.initUnityThread() टाइप करता हूं; यह त्रुटि दिखाता है कि 'UnityThread' वर्तमान संदर्भ में मौजूद नहीं है .. एकता के लिए नए के लिए खेद है ... क्या आप अपने कोड को अधिक विस्तृत तरीके से समझा सकते हैं? ... बहुत धन्यवाद .. – user6142261

+0

आपको एक स्क्रिप्ट बनाना है "यूनिटी थ्रेड" कहा जाता है तो आपको इसके जवाब में 'यूनिटी थ्रेड' कोड कॉपी करना होगा। कृपया मुझे बताएं कि इसका कौन सा हिस्सा कठिन है? – Programmer

+0

'executeCoroutine' को' #if (ENABLE_UPDATE_FUNCTION_CALLBACK) 'के अंदर होना चाहिए या अन्यथा आपको 'executeInUpdate (() => instance.StartCoroutine (action)) पर एक कंपाइलर त्रुटि प्राप्त करने जा रही है;' रेखा जब प्रतीक नहीं है परिभाषित। –

संबंधित मुद्दे