Search Unity

[50USD] Easy threading - Multi threading made easy!

Discussion in 'Assets and Asset Store' started by arklay_corp, Aug 18, 2014.

  1. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    I can confirm that the asset works nicely for WebGL (not like many other assets I had problems in my current project).
     
    AlanMattano and ilmario like this.
  2. StarFluke

    StarFluke

    Joined:
    Jan 19, 2013
    Posts:
    39
    Is there anything special I have to do to make my multi-thread app run in WebGL using Easy Threading?
     
  3. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Nope, it works out of the box
     
    StarFluke likes this.
  4. WilsonC

    WilsonC

    Joined:
    Apr 27, 2013
    Posts:
    3
    Hi Arklay,

    May I know your "Magic" Scripts can be applied to PlayMaker Actions?
     
  5. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    I have never used playmaker, but my magic uses pretty standard potions, so i would say yes, you can apply them to playmaker
     
  6. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    Can you clarify a bit what you mean by "works in WebGL3, since WebGL doesn't support multi threading. Are you juste running all the code that is supposed to be in separate threads in the main thread? If so, how do you make sure it is not stalling the framerate?
     
  7. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    I don't know how unity engine does internally but System.Thread calls work and that's what the plugin is using. Is not as perfomant as webplayer (for example) but it's still playable.
    You can check the differences in this game: https://apps.facebook.com/just_draw_it/ if you open it with chrome you will get webgl, if you open it with any other browser you will get webplayer, both version share the same code
     
  8. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    Well that sounds intriguing, I guess I'll have to explore that a bit more as I didn't expect threads to work at all on WebGL.
     
  9. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    I have to apologize, you are completely right, using this plugin in WebGL can lead to problems. I will address them soon.

    Thanks a lot unisip!
     
  10. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    Oh well, I just purchased your asset, intrigued to test it on WebGL, and saw it didn't work. But as you had answered in this thread, I am ok with that. It is still interesting code to me for other purposes.
    Frankly, I don't really see how this could be fixed in an elegant way, but if you have thoughts on that, I am REALLY interested to hear about them.
    On my end I have been thinking about something slightly different that would involve some kind of special coroutines, running on the main thread, with time slice allocation that would at least prevent hickups.
     
  11. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Sorry to hear that :(

    I don't know how it works but I think you can ask Unity for a refund, I would gladly return it, is not my intention to cheat anyone.

    The quick fix i was thinking about (until unity will support threads on WebGL which should be soon) is to fallback to main thread everything. Should be easy since all the mechanisms are there.

    Basically avoid Task.Run and use instead Task.RunInMainThread
     
  12. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    It's ok, I don't feel cheated, as you had answered my request almost a week ago.
    Seeing how you approached the threading issue in itself in your code is valuable to me, so I don't feel like I deserve a refund.

    As for using Task.RunInMainThread, am I correct in expecting that if the task will be executed in a single Unity frame, resulting in a hickup if it performs anything intensive (like a 2s calculation) ?
    I could probably easily answer my own question by looking at the code but I guess it's good to do it on the forums so that other people interested by your asset have a clearer understanding of what to expect
     
  13. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    In your code you could simply have Task.Run call Task.RunInMainThread if target platform is WebGL. I guess this is what you planned to do anyway
     
  14. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Yeah, that was the quick fix i thought about.

    About the other question, yeah, RunInMainThread will run on Unity's main thread (as Coroutines do) so you should expect a hiccup if you block it for too long. Maybe you can split your intensive process in smaller processes and use Task.WhenAll if they are independent or concatenate them with Task.ContinueInMainThreadWith if you need them to be sequential.

    Another option is measure how long are you blocking the main thread (eg: with a system.diagnostics.stopwatch) and if it's taking too long just save current state and resume it in next iteration using Task.RunInMainThread since if you call Task.RunInMainThread it will be executed in the next frame, not in the current one.
     
  15. fabio1955

    fabio1955

    Joined:
    Nov 11, 2009
    Posts:
    72
    I am developing a wild fire simulator based on cells. From time to time I need to update terrain splat maps and this will produce a pause on the frame rate. The problem with threading is they do not allow unity calls like terrain.SetAlphamaps. Does your implementation allow this? In other terms may I call a task with SetAlphamaps call inside? Thanks
     
  16. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    It sure does, as easy as:
    http://pastebin.com/uNMekrN1
    Code (CSharp):
    1. Task.RunInMainThread(()=>
    2. {
    3.     // everything here is granted to be executed in the main thread
    4.     terrain.SetAlphamaps(1);
    5. });
     
  17. fabio1955

    fabio1955

    Joined:
    Nov 11, 2009
    Posts:
    72
    In this case I should face the same problem because SetAlphamaps needs time to be executed, if it is on the main thread I should have a drop in the FPS...
     
    Last edited: Mar 22, 2016
  18. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    You should keep heavy calculations in background threads, but unity enforces being in the main thread to access its objects, there is no way to workaround that
     
    AlanMattano likes this.
  19. Webbstre

    Webbstre

    Joined:
    Mar 7, 2015
    Posts:
    41
    I also want to know if anyone gets this working with Playmaker
     
  20. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Sorry, I don't own Playmaker so I cannot test it.

    This plugin uses just common mono features so it shouldn't be a problem using it with Playmaker.
     
  21. DrKucho

    DrKucho

    Joined:
    Oct 14, 2013
    Posts:
    140
    Interested on this, im reading and reading and trying to fully understand all this task-thread thing , would you please clarify some stuff to me?

    1.- When you create a Task with your system, that task is executed in it's corresponding and exclusive new Thread , correct?

    2.- A Thread is something modern CPUs have to be able to process stuff at the same time, for real at the same time, not jumping from one routine to another to simulate they are executed at once, correct?

    3.- If point 2 was correct, using one thread to process some data will take double the time than splitting that data in two pieces and using two threads , each one to process each piece, correct?

    4.- If point 2 was correct, there must be a limit of threads running at the same time, given by each specific processor, correct?

    thanks for your time
     
  22. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Of course, I will try:
    It can be queued into an existing ThreadPool (there is one thread per pool). But yes, you can assume it has its own exclusive thread.
    Yes. It has some technical details and in reality is much more complicated but, yes, you can assume that.
    Same as before, it has some technical details, but you can assume that.
    Yes, and there is also a limit of total threads in the system (sleeping or running) but it's quite a big limit (over 1000s in modern mobile phones, much more in pcs)
    You are welcome
     
  23. DrKucho

    DrKucho

    Joined:
    Oct 14, 2013
    Posts:
    140
    thanks for your responses @arklay_corp , threading seems to be so good indeed, mostly thinking that all of my code is being executed in the same main thread, i can imagine the benefits of working with more threads, i do have some heavy routines that could get so much advantage of this but...

    what about Unity engine doing all the physics calculations, ray casts , etc , is Unity Engine also using just one thread for all?
     
  24. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Yes, unity is single threaded, and when you need to access any unity's component it forces you to do so from the main thread.

    That means that you can do all your heavy calculations on background threads but then you have to bring the execution back to the main thread to use unity. Of course this asset offers mechanisms to do it easily:

    Code (CSharp):
    1. Task.RunInMainThread(SomeFunction);
    And, just FYI, coroutines also execute in the main thread
     
  25. DrKucho

    DrKucho

    Joined:
    Oct 14, 2013
    Posts:
    140
    uhmm ... does that mean that i can't use references to unity MonoBehaviour Classes in the threads i create with your keyword Task?

    plus: now that you mention this --> Task.RunInTheMainThread(SomeFunction);

    i don't understand it, i didn't understand it first time i read it on your instructions, i think i am missing something cause... why a instruction for that? why would i want to run a task in main thread when all my code is running in the main thread already , so to me to do Task.RunInMainThread(SomeFunction) means exactly the same as simple method call --> SomeFunction()
     
    tolosaoldfan likes this.
  26. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Means that you can use task and threads and do your calculations, but the moment you want to access some unity component you have to do so from the main thread, for example:


    Code (CSharp):
    1.  
    2.         public UnityEngine.UI.Text resultTextBox;
    3.  
    4.         void DoSomeHeavyCalculation()
    5.         {
    6.             Task.Run(DoTheCalculation);
    7.         }
    8.  
    9.         void DoTheCalculation()
    10.         {   // This will execute in a background thread
    11.             float result = 0;
    12.             //
    13.             // Here you do some cpu intensive calculation
    14.             // that takes long time, to calculate result some how
    15.             //
    16.  
    17.             // ...
    18.  
    19.             //
    20.             // Now you want to show the result in a Text component,
    21.             // you cannot do it directly because you are in a
    22.             // background thread
    23.             //
    24.  
    25.             // This would raise an exception
    26.             // resultTextBox.text = "The result is " + result.ToString();
    27.  
    28.             // So you need to do:
    29.             Task.RunInMainThread(() => resultTextBox.text = "The result is " + result.ToString());
    30.         }
     
    tolosaoldfan likes this.
  27. DrKucho

    DrKucho

    Joined:
    Oct 14, 2013
    Posts:
    140
    aaaah i seee... the Task.RinInMainThread() is something you do INSIDE the background thread, that makes total sense and i kinda feel stupid now LOL

    but you might want to consider to rewrite your instructions on first post cause i would say it is confusing, if you going to explain how to work background threads , i believe the logical way would be to first explain how to create and start a background thread, not how to "exit" from background thread , to me that's kind of reversed order and that's why i got confused.
     
    tolosaoldfan likes this.
  28. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Good idea, I will keep that in mind, thanks! :)
     
    AlanMattano likes this.
  29. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    Hi arklay_corp,

    I juste started playing with your package and I love it.
    Just my two cents, I think there is a bug in the Dispatcher class --> access to the action queue m_q is not threadsafe. Dequeuing and accessing Count is not really problematic but my tests show that enqueuing causses issues. Anyway I just added a lock on every call to m_q.Enqueue and the problem is fixed. Thought I'd let you know so you can fix it in a future release too.
     
  30. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    oh! I will check it and fix it, you are completely right!

    Thanks a lot unisip!
     
  31. DrKucho

    DrKucho

    Joined:
    Oct 14, 2013
    Posts:
    140
    question: can we get the API docs? it would help me to understand how to work it before purchase.
     
  32. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
  33. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    Hi arklay_corp,

    I am now using EasyThreading intensively and I have some concerns about pooling. My use case is that I need to run lots of fairly small jobs (each taking something like 50ms or so), and the way the system is designed doesn't seem to reuse threads so much in that context -> a thread that completes its task and doesn't find other tasks waiting in the queue simply dies.

    I was thinking about modifying it to have threads sleep while waiting for new jobs in the queue, but before I do that I'm interested in hearing your thoughts about that? Is there a specific reason you decided not to use that approach ?
     
  34. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Hi unisip,

    That's exactly my approach for the thread pool. Make sure you have the latest version of the plugin and check the class "ThreadPool", it keeps a queue of actions to be performed and when one finishes it looks for the next one instead of creating a new thread.
     
  35. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    340
    That's correct, but in fact in practice it turns out that if tasks as rather short, it will keep creating many more threads than max_threads because lots of the existing threads will simply "die" when the queue is empty.
    By design, assume you have max_threads=10 and you submit 100 short tasks (execution time around 50ms). What happens is that the first ten jobs will trigger the creation of 10 threads. However, since the creation time for threads adds up, it is very likely that the first job will finish before the 10th thread is created. That first thread will complete and look for a task in the queue at a moment where the queue is still empty because not all threads have been created. As a result, the first thread will simply die. When the 11th task is submitted, it will hence create a new thread, somehow defeating the purpose of the pool.

    This is why I mentioned the other approach, which implied creating all the threads upfront and keeping them alive with Thread.sleep, and having them check for tasks waiting in a queue. Whenever a new task is submitted it is sent to the queue directly and will be picked up by a waiting thread. I believe this would be more efficient in the sense that there is never a cost of creating threads, except at the beginning.
     
  36. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Ok, you are right on your case maybe you can leve the thread blocked using ManualResetEvent.WaitOne when the action finishes and there is no action in the queue and wake the thread up with ManualResetEvent.Set once you get a new action.
     
  37. Velcrohead

    Velcrohead

    Joined:
    Apr 26, 2014
    Posts:
    78
    Hi,

    I've been using my own threading system for the last couple of months to handle a lot of the world generation - it works pretty well but I'm having a terrible time trying to debug it.

    Is your package capable of displaying any errors that may occur within the thread?

    Look forward to hearing from you, thanks!
     
  38. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Hi Velcrohead,

    The problem you are facing is VERY common when working with threads in unity, because unity WON'T log the exception happening inside a thread in the log.

    You can catch them and show them manually using "Debug.LogException" or use the Task.Exception field, that's up to you.

    Thanks to you for using my plugin :)
     
  39. TheCode

    TheCode

    Joined:
    Dec 24, 2013
    Posts:
    7
    Hi there!
    I have a question about performance. Does work on Main thread will executed on next Update or it can be running kind of immediately? My situation is that I have some script in parallel thread and it used several Raycast which should run on main thread only. But as I understand each call of Raycast by this method will wait until queue on main thread will be able to cast the ray and all work in my script will be delayed. And if I have 100 raycasts in cycle I will wait until all this job will be done. I tried it by handy method and its pretty slow (40 raycasts takes 1 second). And I cannot combine all raycasts into one job, they all in cycle. So the question is how this easy threading will manage with it?
    Thanks!
     
  40. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Hi TheCode,

    When you send something to the main thread it will wait for the main thread to be executed. By default in the update cycle, but you can change to something that executes more often.

    Also you can adjust how many "queued actions" will the main thread execute in each cycle. Let's say you have 100 raycast queued, but if you execute all of them in the same update cycle the main thread will get blocked too long, so you can limit and execute just 10 raycasts (queued actions) each cycle.

    So if what you need is to "space" all this raycast over time for the main thread not to get blocked, yes, you can do it.
     
  41. TheCode

    TheCode

    Joined:
    Dec 24, 2013
    Posts:
    7
    Thanks Arklay, I'll purchase it :)
     
  42. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Thanks to you!

    mmmm beer!
     
  43. TheCode

    TheCode

    Joined:
    Dec 24, 2013
    Posts:
    7
    Hi again!

    Beer is on the way :)
    I dont understand one thing: When I need from some thread do the task from main thread and return the value and only after that proceed, what I need to use? I tried this:
    Task<Vector3>.RunInMainThread(() =>
    {
    return Ray(from, to);
    }).ContinueWith((t) => {
    Debug.Log(t.Result);
    });
    and this debug info appear After job is done, not when I need it. How I can wait until guaranteed result of main thread execution (and fastest execution if any options available)?

    thanks :)
     
  44. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Probably is because of the context of the variables from and to. Try not using a lambda expression, or, even create a function for the whole process to ensure those variables are in the scope you need:

    Code (CSharp):
    1. void DoProcess(Vector3 from, Vector3 to)
    2. {
    3.     Task<Vector3>.RunInMainThread(() =>
    4.     {
    5.         return AuxProcessInMainThread(from, to);
    6.     }).ContinueWith((t) =>
    7.     {
    8.         Debug.Log(t.Result);
    9.     });
    10. }
    11. Vector3 AuxProcessInMainThread(Vector3 from, Vector3 to)
    12. {
    13.     return Ray(from, to);
    14. }
    [Note that i didn't even compile that code, it may have some small errors]

    With that code it should work... if you don't like the code overhead you can try to remove some method... I'm sorry I cannot be more specific, I got lost with these scope problems and I always solve them by trial and error...
     
  45. TheCode

    TheCode

    Joined:
    Dec 24, 2013
    Posts:
    7
    I do just the same, ContinueWith just not waits until result is done, I see it by debug info, it goes in parallel. Unity 5.4, this part of code running by your Task func in the thread also... and this is in cycle:

    Debug.Log("start with " + from);
    Task<Vector3>.RunInMainThread(() =>
    {
    return Ray(from, to);
    }).ContinueWith((t) =>
    {
    Debug.Log("RESULT" +t.Result);
    });
    Debug string is messy: start with,start with,start with, RESULT,start with,start with,RESULT
     
  46. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    your output makes sense, the task for the main thread doesn't get executed right away, it has to wait for the main thread to be executing.

    Code (CSharp):
    1. Debug.Log("1");
    2. Task<Vector3>.RunInMainThread(() =>
    3. {
    4.     Debug.Log("2");
    5.     return Ray(from, to);
    6. }).ContinueWith((t) =>
    7. {
    8.     Debug.Log("3");
    9. });
    10. Debug.Log("4");
    Will produce an output similar to:
    1423

    Of course "RunInMainThread" goes in parallel with the thread that created it, because they are different threads
     
  47. TheCode

    TheCode

    Joined:
    Dec 24, 2013
    Posts:
    7
    yes indeed but how I can hold execution of current thread until RunInMainThread return value? So I need to get result as 1234
    I thought with your plugin I can do it :)
     
  48. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    It's not a good idea to block a thread like that (it's better to design using continuation actions) but if you want to do it you don't need any plugin for that:
    Code (CSharp):
    1. ManualResetEvent sem = new ManualResetEvent(false);
    2. Debug.Log("1");
    3. Task<Vector3>.RunInMainThread(() =>
    4. {
    5.     Debug.Log("2");
    6.     return Ray(from, to);
    7. }).ContinueWith((t) =>
    8. {
    9.     Debug.Log("3");
    10.     sem.Set();
    11. });
    12. sem.WaitOne();
    13. Debug.Log("4");
     
  49. TheCode

    TheCode

    Joined:
    Dec 24, 2013
    Posts:
    7
    yes I know but this is very slow :(
     
  50. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Instead of this:
    Code (CSharp):
    1. Vector3 rayResult;
    2. ManualResetEvent sem = new ManualResetEvent(false);
    3. Debug.Log("1");
    4. Task<Vector3>.RunInMainThread(() =>
    5. {
    6.     Debug.Log("2");
    7.     return Ray(from, to);
    8. }).ContinueWith((t) =>
    9. {
    10.     Debug.Log("3");
    11.     rayResult = t.Result;
    12.     sem.Set();
    13. });
    14. sem.WaitOne();
    15. Debug.Log("4");
    16. DoSomeStuffWithTheResultFromTheRay(rayResult);
    Do something like this:
    Code (CSharp):
    1. Debug.Log("1");
    2. Task<Vector3>.RunInMainThread(() =>
    3. {
    4.     Debug.Log("2");
    5.     return Ray(from, to);
    6. }).ContinueWith((t) =>
    7. {
    8.     Debug.Log("3");
    9.     Vector3 rayResult = t.Result;
    10.     DoSomeStuffWithTheResultFromTheRay(rayResult);
    11.     Debug.Log("4");
    12. });