Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Not working - Expecting synchronous execution of statements in Unity3D.

Discussion in 'Scripting' started by Anindya-Nishant, Sep 23, 2016.

  1. Anindya-Nishant

    Anindya-Nishant

    Joined:
    May 2, 2014
    Posts:
    13
    It is not a good way to implement this but the scenario is similar to our requirement.

    I have a Class which has a method "ExecuteSync". The code snippet of the class containing the function is as follows:

    Code (CSharp):
    1.  
    2. public class MyTasksClass : Monobehaviour{
    3.      public void ExecuteSync(string actionName){
    4.           switch(actionName){
    5.           case "run" :
    6.              StartCoroutine(ActionOne());
    7.           break;
    8.           }
    9.      }
    10.  
    11.     IEnumerator ActionOne(){
    12.          Debug.Log("Action Run Started");
    13.          //the time of animation is 10 seconds.
    14.          yield return new WaitForSeconds(10f);
    15.          Debug.Log("Action Run Ended");
    16.     }
    17. }
    18.  
    There is another class UIController from which we can call these methods like as follows:

    Code (CSharp):
    1. public class UIController : Monobehaviour{
    2.        public MyTasksClass myTaskClass;
    3.        void Start(){
    4.                myTaskClass = FindObjectOfType<MyTasksClass>();
    5.    
    6.                Debug.Log("Synchronous started");
    7.                myTaskClass.ExecuteSync("run");
    8.                Debug.Log("Synchronous ended");
    9.        }
    10.   }

    The expected output should print in this order:
    1. Synchronous started
    2. ActionOneStarted
    3. ActionOneEnded
    4. Synchronous ended
    But the actual output is as follows:
    1. Synchronous started
    2. Synchronous ended
    3. ActionOneStarted
    4. ActionOneEnded
    What we want :
    The Log "Synchronous Ended" should NOT get printed unless and until the entire execution of Execute method is completed. Is there any way to block the execution of the line below 'myTaskClass.ExecuteSync' unless the execution i.e, the playing of animation of 10 seconds is completed ?
    We are expecting the synchronous behaviour with this, but it is not working.

    Is it even possible for the above statement to execute in a synchronous way to print the expected output ? Any help will be appreciated.

    Thanks,
     
  2. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    so, the action you are asking for requires the finish of the coroutine, to do this, you need to use Start as a coroutine and yield the called coroutine.

    Code (csharp):
    1. public class UIController : Monobehaviour{
    2.        public MyTasksClass myTaskClass;
    3.        IEnumerator Start(){
    4.                myTaskClass = FindObjectOfType<MyTasksClass>();
    5.    
    6.                Debug.Log("Synchronous started");
    7.                yeild return myTaskClass.ExecuteSync("run");
    8.                Debug.Log("Synchronous ended");
    9.        }
    10.   }
     
  3. Anindya-Nishant

    Anindya-Nishant

    Joined:
    May 2, 2014
    Posts:
    13
    Hi, thanks for your response.
    But is there any other way than doing it with co-routines? Our requirement is to achieve this functionality without using co-routines.
     
  4. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    There really isn't. Though I find this statement odd:
    Because your original posted code uses Coroutines. Why does your requirements require that you not use Coroutines?

    To explain why your first code failed whats happening is this:
    • UIController has its Start function called by Unity
    • It then prints the Debug.Log Syncchronous Started
    • It calls MyTaskClass executeSync.
    • MyTaskClass ExecuteSync Starts the Coroutine Action One (this starts a separate thread)
    • Program Control returns to UIController it prints out Synchronous ended / Thread Action One Prints Action one Started (these two things are going on at the same time)
    • Program Returns flow back to Unity at the end of Start (Unity will now call the next Script's Start()),/ Thread Action one starts to wait for 10 seconds
    • Unity and your program do whatever they do for the next 10 seconds, calling the rest of the Starts and then Updates each frame
    • Finally 10 seconds pass Thread Action One Calls Debug Action One ended and thread is stopped.
    There are basically only two ways to have multiple sets of code running synchronously. Coroutines and System Threads. Coroutines are just Unity's hook into threading. They proved quick and easy use of them as well as allowing you to safely call all of Unity's API from within that thread. Using System Threading is much more complicated and Unity API calls are not safe inside of them. So should only be used for very specific purposes.
     
  5. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Not true! Coroutines still run in the main thread and are not concurrent. They simply provide a mechanism to spread the execution of instructions out over multiple frames.
     
    takatok likes this.
  6. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    @KelsoMRK thanks for the clarification. So Coroutines are just a set of functions that Unity is calling like Update() each frame? Though with the extra functionality that they can interrupt their flow control in the middle of the function and come back at a later time based on the yield. I had always assumed they were threaded. Makes sense now why the API is safe inside them and not threads.
     
  7. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Coroutines are evaluated every frame (if they're active of course). They execute until they encounter a yield instruction or complete. The current yield instruction is evaluated every frame to see if it's condition is satisfied and the coroutine can continue execution. IEnumerator is the same thing that powers foreach loops against collections - it's just a clever application in this context. You can actually write your own generic coroutine handler which is great for executing IEnumerator yielded logic from classes that aren't MonoBehaviours

    Code (csharp):
    1.  
    2. public class SomeClass
    3. {
    4.     IEnumerator DoSomething()
    5.     {
    6.         while (true)
    7.         {
    8.             Debug.Log("I'm working here!");
    9.             yield return null;
    10.         }
    11.     }
    12. }
    13.  
    14. public class RoutineRunner : MonoBehaviour
    15. {
    16.     public IEnumerator Run(IEnumerator cor)
    17.     {
    18.         while (cor.MoveNext())
    19.             yield return cor.Current;
    20.     }
    21. }
    22.  
    23. runner.StartCoroutine(runner.Run(someClass.DoSomething()));
    24.  
     
  8. Anindya-Nishant

    Anindya-Nishant

    Joined:
    May 2, 2014
    Posts:
    13
    Hi Guys,
    Thanks for your opinions.
    I think this topic got diverted because i have used a co-routine as an example here.

    The task that I want to achieve is something like this..

    Debug.Log("Start animation");
    //Do a task that lasts for say 10 seconds.
    //Play an animation
    animation.Play(); // Psedo code only.
    Debug.Log ("End Animation");

    Now the end animation log should get printed only and only if the animation gets completed. I need some blocking mechanism here but unfortunately, I can't use co-routines because to make the execution synchronous in unity, we will need co-routine (say A) which contains a co-routine that does actual stuff (say B). Now A again has to be inside another co-routine and so on..

    So, how can we achieve this?
     
  9. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    Animations can have events associated with them. You can have a function called when Animation ends so you could easily have only one Coroutine involved:
    Code (CSharp):
    1. // Some code calls:
    2. StartCoroutine(TaskAnimate());
    3.  
    4. IEnumerator TaskAnimate()
    5. {
    6.       // task that takes 10 minutes
    7.       // with appropriate yield returns
    8.  
    9.       // After thats done
    10.       animation.Play();
    11. }
    12.  
    13. // then hook then end of your Animation to call this function
    14. void AnimationFunction()
    15. {
    16.         Debug.Log("End Animation");
    17.          // or whatever else you need to do
    18. }
    19.  
     
  10. Anindya-Nishant

    Anindya-Nishant

    Joined:
    May 2, 2014
    Posts:
    13
    Again, this wont solve the purpose that I am looking for as I have said in my previous comment, I need some blocking mechanism to implement this feature . Strictly NO COROUTINES because , to make the execution synchronous in unity, we will need co-routine (say A) which contains a co-routine that does actual stuff (say B). Now A again has to be inside another co-routine and so on..
     
  11. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    This makes no sense to me. What exactly are you doing that you think it would require infintely nested coroutines?
     
  12. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    So you just need to wait until an animation is done playing?
    Code (csharp):
    1.  
    2. IEnumerator Wait()
    3. {
    4.     Debug.Log("start");
    5.     animation.Play();
    6.     while (animation.isPlaying)
    7.         yield return null;
    8.     Debug.Log("end");
    9. }
    10.