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.

Unity Threading Helper

Discussion in 'Assets and Asset Store' started by Marrrk, May 21, 2011.

  1. Marionette

    Marionette

    Joined:
    Feb 3, 2013
    Posts:
    349
    https://www.dropbox.com/s/eys0hy4s4k6cdr9/threading.wmv

    this video shows the 10 item blast and locking unity I was talking about:
    https://www.dropbox.com/s/5vljv1gpw7g3z9c/threading2.wmv

    so here's what's going on. (please excuse the jittery nature of the video, the capture software I used, blows.)

    first of all, I added:

    Code (csharp):
    1. internal string taskID = Guid.NewGuid().ToString();
    to the task classes so that I can see what tasks are what etc..

    this is the code i'm using.

    Code (csharp):
    1. //20 tasks
    2.             for (int t = 0; t < 20; t++)
    3.             {
    4.                 //add them
    5.                 _distributor.Dispatch(() =>
    6.                 {
    7.                    
    8.  
    9.                     //work
    10.                     //******************************
    11.                     int[][] arrays = new int[random.Next(500, 1000)][];
    12.                     for (int i = 0; i < arrays.Length; ++i)
    13.                     {
    14.                         arrays[i] = new int[random.Next(500, 10000)];
    15.                         for (int k = 0; k < arrays[i].Length; ++k)
    16.                             arrays[i][k] = random.Next(10000);
    17.                     }
    18.  
    19.                     for (int i = 0; i < arrays.Length; ++i)
    20.                     {
    21.                         var index = i;
    22.  
    23.  
    24.                         System.Array.Sort(arrays[index]);
    25.                         System.Threading.Interlocked.Increment(ref sortedArrays);
    26.  
    27.                         if (sortedArrays == arrays.Length)
    28.                         {
    29.                             isCalculating = false;
    30.                             sortedArrays = 0;
    31.                         }
    32.                     }
    33.                     //********************************
    34.  
    35.                     //when this task is done, update the GUI with our results
    36.                 }).WhenEnded((a) =>
    37.                 {
    38.                     //i need to use this one because the _dispatcher i created
    39.                     //doesn't have the main thread..
    40.                     UnityThreadHelper.Dispatcher.Dispatch(() =>
    41.                     {
    42.                         string name = string.Format("Task {0} complete", a.taskID);
    43.                         GameObject obj = new GameObject(name);
    44.                     }).Wait();
    45.                 });
    46.             }
    47.  
    48.             _distributor.Start();
    so my questions:
    • where are the first tasks?
    • why is it processing sequentially?
    • where are the missing tasks?
    • why doesn't the distributor I created have the main thread?

    my understanding would be, add 20 tasks and part of each task would be to 'update itself' using the WhenEnded extension. when each thread is complete, look at the tasklist and grab the next task. if there are 4 threads available, the remaining tasks should be split across those threads. since I vary the amount of work being done, all the threads should *not* complete at the same time, however you can see what's going on. if I expose the isAlive via the distributor, after the first 8 threads fire off, only 1 thread is left alive that then processes what's left of the tasklist. I have no clue what happened to the first 8-10 as it varies.. I never get any notification from them..

    if I click restart a few times, sometimes they will process all 20, but always sequentially. other times, it just blasts 10 items and it doesn't look like anything processed.

    additionally, even though I dispose of the distributor in the OnDestroy method, it makes unity hang occasionally. especially if I stop the play mode before it finishes. it always calls my dispose in OnDestroy however.

    if you need more videos let me know.

    please advise...
     
    Last edited: Feb 10, 2014
  2. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    I am currently in Paris and will be able to check this next friday. But you can try to change one thing in the TD code. There is an IsolateTasks call which uses a variable count of tasks to isolate max 3 tasks. You can change this variable to be always 1. Maybe this helps a little.
     
  3. Marionette

    Marionette

    Joined:
    Feb 3, 2013
    Posts:
    349
    will do.

    btw, do I have it correct with the way it's supposed to work? my assumption would be that 'WhenEnded' would spin until called thus making that part sequential, correct?

    also, i'm getting weird errors as well, like 'FindObjectsOfType' in the TD code that complains because it's not called on the main thread.
     
  4. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    I found the issue, your code producs in some cases exceptions, which will kill the worker thread they are working on, which results in less working worker threads and in some cases other issues, like the blocking of the complete editor.

    This is the exception I receive:

    This is the script I use:
    http://ideone.com/QUAuax

    The failing line points to this:
    Code (csharp):
    1.  
    2. for (int k = 0; k < arrays[i].Length; ++k)
    3.   arrays[i][k] = random.Next(10000); // ERROR
    4.  
    The exception points to random.Next(..) and not to the setter so I assume that the random class of Mono is not threadsafe, you may create the instance inside the Task or lock the usage which may yield to the result you expect.

    Also you create very large arrays, the computation may take a while depending on the random.Next results.

    I added better log messages in my development version and changed the way tasks will operate so that the worker thread will not die through an unhandled exception (The dispatcher class has some methods which will produce safe actions/funcs, but this will now be obsolete as I think the way it will be is better).

    The FindObjectsOfType error may come through a missing first UnityThreadHelper.EnsureHelper(); call before you use any threads (this is now needed if you directly use the UnityThreadHelper) or through an butterfly effect resulting from the error in the task.

    WhenEnded will be called AFTER the task work has been done and BEFORE any blocking Wait call will end, regardless of the state of the task.
    WhenSucceeded will be called under the same conditions except that it will be called only when the task has been ended successfully and has NOT been aborted.
    WhenFailed is the same as above, but only when the task has been aborted or has been failed (exception).
    OnResult is the same as WhenSucceeded, but will use the given delegatewith the result value of the task which has been ended as parameter.

    In your sample, WhenEnded will not provide you any benefits, as you can call directly the Dispatcher from the tasks core code.

    Alternatively you can do this:

    Code (csharp):
    1.  
    2. task.WhenEnded(() =>
    3. {
    4.   GameObject obj = new GameObject(task.Name);
    5. }, UnityThreadHelper.Dispatcher);
    6.  
    btw, you can use the Task.Name field for your GUID tests

    Greetings, Mark
     
  5. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    I added a new version with some changes:
    - Adding a delegate to the Task.TaskEnded event will now work properly when using this from different threads.
    - Added WhenFailed, WhenSucceeded and Then to IEnumerable<Task>.
    - Unhandled exceptions thrown while processing a task will no longer kill the working worker thread of a task distributor
    - Unhandled exceptions thrown while processing a task will now automatically set the task to the Failed state and will error log the exception details
    - some smaller changes

    The AssetStore version will be updated too, but this may take some time.
     
  6. Marionette

    Marionette

    Joined:
    Feb 3, 2013
    Posts:
    349
    thank you man, it appears to be working as I expected now ;)
     
  7. SullyTheStrange

    SullyTheStrange

    Joined:
    May 17, 2013
    Posts:
    147
    Hey Marrrk, I'm just getting started using your plugin (looks awesome, by the way), but I'm getting an error by just copy and pasting this code from your first post:

    Code (csharp):
    1. UnityThreading.ActionThread myThread;
    2.  
    3.  void Start()
    4. {
    5.  myThread = UnityThreadHelper.CreateThread(DoThreadWork);
    6. }
    7.  
    8. void DoThreadWork()
    9. {
    10.  // processing
    11. }
    And the error I'm getting is:

    Code (csharp):
    1. error CS0121: The call is ambiguous between the following methods or properties: `UnityThreadHelper.CreateThread(System.Action)' and `UnityThreadHelper.CreateThread(System.Func<System.Collections.IEnumerator>)'
    Any idea what I'm doing wrong? It's a C# script, in 4.3.3f1 if that matters.
     
  8. Marionette

    Marionette

    Joined:
    Feb 3, 2013
    Posts:
    349
    do either this:

    UnityThreadHelper.CreateThread(()=>
    {

    //do work here

    });

    or:

    IEnumerator DoWork()
    {

    //work as you would a coroutine..

    }

    and pass that to: UnityThreadHelper.CreateThread(DoWork);

    the arg for that function doesn't take a method, it takes an Action or IEnumerator
     
    Last edited: Mar 6, 2014
  9. SullyTheStrange

    SullyTheStrange

    Joined:
    May 17, 2013
    Posts:
    147
    Okay... Not sure why the example is showing the incorrect way of doing things, then. :| But thanks.

    To work around it before you had answered, the way I set it up was by creating a thread that simply calls another function, like the example below. Is there a problem with doing this? It seems to be working fine.

    Another random question, if there's a runtime error inside of a thread, it won't throw an error message, is that right? I've ran into another problem where an enemy whose pathfinding is in a thread will just stop working, and I'm assuming there's an error that it isn't telling me about.

    Code (csharp):
    1. void Start() {
    2.    myThread = UnityThreadHelper.CreateThread(() => {
    3.       DoWork();
    4.    });
    5. }
    6.  
    7. void DoWork() {
    8.    // stuff
    9. }
     
  10. Marionette

    Marionette

    Joined:
    Feb 3, 2013
    Posts:
    349
    i think while fixing one of the issues i had bumped into, he said something about how the thread wouldn't just fail now and would report the error.

    btw, calling that method inside of an action, is the same thing as just calling the action ;)

    IE:

    Code (csharp):
    1.  
    2. void Start()
    3. {
    4.     myThread = UnityThreadHelper.CreateThread(() =>
    5.     {
    6.  
    7.          //alternatively, you can just define the work here instead of having to call
    8.          //out to another method if you like. theoretically, i wouldn't think
    9.          //you'd have anything that would call that method other than
    10.          //the thread func, right?
    11.  
    12.          //DoWork();
    13.  
    14.  
    15.      });
    16. }
    17.  
    18. void DoWork() {
    19.  
    20.  
    21. // stuff
    22. }
    23.  
    24.  
    25.  
     
  11. itsMeDOUG

    itsMeDOUG

    Joined:
    Dec 21, 2010
    Posts:
    26
    Hey, great plugin, it has been very useful in putting the code from my voxel-like engine into another thread to load chunks. I have run into an issue where the bottom code spits out an error in the console before it is run. The function GenSquare is called inside of a thread by another function, but I need the lines with noise.GetValue to be in the main thread since the asset (Coherent Noise) does not seem to be thread safe. When I either try to send each line individually to the dispatcher or as a function (UnityScript), it gives the below error:

    Code (csharp):
    1. Assets/Scripts/Chunk.js(56,46): BCE0023: No appropriate version of 'UnityThreading.DispatcherBase.Dispatch' for the argument list '(error)' was found.
    Code (csharp):
    1. function GenSquare(x:int, y:int, z:int, texture:Vector2) {
    2.     UnityThreadHelper.Dispatcher.Dispatch(function() {
    3.         newVertices.Add(new Vector3
    4.             (x,
    5.             noise.GetValue(x+(chunkSize*(chunkX/64)) , z+(chunkSize*(chunkZ/64)) , 0)*chunkHeightVariance,
    6.             z));
    7.         newVertices.Add(new Vector3
    8.             (x+1,
    9.             noise.GetValue(x+(chunkSize*(chunkX/64))+1 , z+(chunkSize*(chunkZ/64)) , 0)*chunkHeightVariance,
    10.             z));
    11.         newVertices.Add(new Vector3
    12.             (x+1,
    13.             noise.GetValue(x+(chunkSize*(chunkX/64))+1 , z+(chunkSize*(chunkZ/64))-1 , 0)*chunkHeightVariance,
    14.             z-1));
    15.         newVertices.Add(new Vector3
    16.             (x,
    17.             noise.GetValue(x+(chunkSize*(chunkX/64)) , z+(chunkSize*(chunkZ/64))-1 , 0)*chunkHeightVariance,
    18.             z-1));
    19.     });
    20.     newTriangles.Add(squareCount*4);
    21.     newTriangles.Add((squareCount*4)+1);
    22.     newTriangles.Add((squareCount*4)+3);
    23.     newTriangles.Add((squareCount*4)+1);
    24.     newTriangles.Add((squareCount*4)+2);
    25.     newTriangles.Add((squareCount*4)+3);
    26.    
    27.     newUV.Add(new Vector2 (tUnit * texture.x, tUnit * texture.y + tUnit));
    28.     newUV.Add(new Vector2 (tUnit*texture.x+tUnit, tUnit*texture.y+tUnit));
    29.     newUV.Add(new Vector2 (tUnit * texture.x + tUnit, tUnit * texture.y));
    30.     newUV.Add(new Vector2 (tUnit * texture.x, tUnit * texture.y));
    31.    
    32.     squareCount++;
    33. }
    Not sure if this is a mistake on my part or an error in the plugin, any help would be appreciated. I could work around this by putting the whole function into the dispatcher, but I can't pass in my variables that way, and if I dispatch it before the variables are even passed in (further back into the threaded functions), it cuts down much of the benefit of the thread.
     
  12. Marionette

    Marionette

    Joined:
    Feb 3, 2013
    Posts:
    349
    i'd try to help, but i'm allergic to javascript ;(

    kidding ;)

    first, make sure you are using the latest version. i think he updated a couple of weeks ago, making sure you backup/remove the other version first, to make sure you don't have anything to possibly conflict the new version.

    also try appending a .Wait() to see if that helps.. i found in some cases if you don't wait, you'll get a race condition, especially if you needed something to be created on the main thread first, before modifying something in the worker thread.
     
  13. itsMeDOUG

    itsMeDOUG

    Joined:
    Dec 21, 2010
    Posts:
    26
    As far as I know, I am on the most current version, since the download I got was yesterday. I have tried to add a .Wait(); but the error remains, unless I should be adding it differently. I believe I have tried each of the different ways to call a dispatcher, its almost as if it doesn't like the UnityScript since instead of stating a type in the argument list it just states "error". Either that or I am doing something stupid :roll:
     
  14. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    @SullyTheStrange:

    Yeah the start post is not very up to date/clear. You can do it like Marionette proposed or as you already do it. There is no negative aspect in doing it that way.

    Exceptions will be catched and reported (UnityEngine.Debug.LogError(exception)), but nothing more will be done.

    @itsMeDOUG:

    Well I am no UnityScript expert but I try my best, you can try this:

    Code (csharp):
    1.  
    2. var myFunc : Function = function() { /* your code */ };
    3. UnityThreadHelper.Dispatcher.Dispatch(myFunc);
    4.  
    or:

    Code (csharp):
    1.  
    2. var myFunc : System.Action = function() { /* your code */ };
    3. UnityThreadHelper.Dispatcher.Dispatch(myFunc);
    4.  
    I am not really sure what type an anonymous function will generate, if I find out what it is I might be able to build a workaround for JS functions.

    Btw noise functions are really expensive when doing them many times, so you should really look into a solution to make this runnable in a per thread base. I guess you only need to create an instance per thread/task and it will work.
     
  15. Developer386DX

    Developer386DX

    Joined:
    Mar 17, 2014
    Posts:
    1
    Hi works very well , but what about compatibility between platforms , IOS , ANDROID etc ?
     
  16. Atanvaryar

    Atanvaryar

    Joined:
    Sep 1, 2012
    Posts:
    9
    Hi Marrrk,
    I've recently bought and implemeneted your ThreadHelper in my project, and it works awesomely so far except a few tiny things I can't figure out myself :(
    I'm using Tasks and adding them to TaskDistributor queue. Everything works fine in editor mode, and even in standalone windwos mode, but once I get to Mac standalone I seem to be getting random NULL exception errors which I can't put my finger on.
    Sometimes the mac standalone works correctly, but often it just freezes.

    Could you possibly offer some insight as to what it may be linked to?
     
  17. sonicviz

    sonicviz

    Joined:
    May 19, 2009
    Posts:
    1,046
    Any news on this?
    I want to try this package as I'm also having strange problems with the LOOM threading framework on OSX as well, but if you are getting similar strange issues maybe it's a Unity threading / OSX issue?
     
  18. sonicviz

    sonicviz

    Joined:
    May 19, 2009
    Posts:
    1,046
    And does it or not? There is no indication in the Asset Store description as to what platforms it works with.
     
  19. Atanvaryar

    Atanvaryar

    Joined:
    Sep 1, 2012
    Posts:
    9
    Marrrk hasn't been online for a week, and hasnt replied to this thread in three weeks.
    Either he is very busy, or he is kinda ignoring this project, which is sad either way :(
    I guess we will have to wait for Unity 5 with it's native multi-threading solutions.
     
  20. sonicviz

    sonicviz

    Joined:
    May 19, 2009
    Posts:
    1,046
    He finally replied to my email I sent direct.

    What's that about Unity5 MT? Link?
     
  21. Atanvaryar

    Atanvaryar

    Joined:
    Sep 1, 2012
    Posts:
    9
  22. sonicviz

    sonicviz

    Joined:
    May 19, 2009
    Posts:
    1,046
  23. Atanvaryar

    Atanvaryar

    Joined:
    Sep 1, 2012
    Posts:
    9
  24. sonicviz

    sonicviz

    Joined:
    May 19, 2009
    Posts:
    1,046
    That's good, but just seems to say Unity automagically will be more multithreaded now.

    I wonder how much control we'll have over it though?
     
  25. dmitche3

    dmitche3

    Joined:
    Apr 3, 2014
    Posts:
    23
    From a previous posting by Mark:

    Mark makes reference to a "TaskBase" yet there is no declaration.

    To anyone: An answer?
    OR
    DO I wait for Unity 5 threading?
    OR
    Will this could allow a thread to perform a wait for a signal, such as an autoresetevent or semaphore? I need to start a thread and have it wait for a signal to continue processing.
     
    Last edited: Apr 14, 2014
  26. ldw-bas

    ldw-bas

    Joined:
    Jul 15, 2013
    Posts:
    1
    Hi!

    Your library looks very nice! But I have got a couple of questions. I have created code that created loads of micro tasks (in the order of 10000). I had this running on my own solution (since I was disappointed in the .net ThreadPool), but when searching for some more information I came across this library. I was trying to port my jobs to this solution. It seems to go very slow, processing everything sequentially on a single core seems to be much faster. The profiler also give me weird results when I try to profile the issue. Adding to the dispatcher takes more than 100% for example 300% of the time. My wild guess is the setting WaitHandle Events is causing this.

    Has anyone else tried executing this amount of jobs, with this system?
     
  27. Sybaris

    Sybaris

    Joined:
    Aug 19, 2013
    Posts:
    91
    Hi Marrrk,

    thanks for this fabulous library! It saved me hours of time. Excellent work!

    If my project ever sees the light of day then I would like to give you credits - how would you want to be credited? Simply with "Marrrk"?

    An idea for those who work with the library: I had the problem that my background task sometimes dispatched a lot of calls to the main thread, which caused hickups because the tasks were processed on the main thread in ONE frame. I solved my problem by changing the method ProcessTasksInternal() in Dispatcher.cs to only process one task per frame. Now everything runs smooth. Maybe this helps somebody.

    Regards,

    G.
     
  28. realghetto

    realghetto

    Joined:
    Jan 21, 2014
    Posts:
    112
    what happened to the UnityThreading.ThreadSafe class? I can't seem to create thread safe gameobjects anymore.
     
  29. ProgramUrFace

    ProgramUrFace

    Joined:
    Sep 10, 2013
    Posts:
    10
    I just finished implementing this and my god did it help.
    I am only using one thread and not including the main thread. and this is so so much faster in my terrain engine.
     
  30. Zyxil

    Zyxil

    Joined:
    Nov 23, 2009
    Posts:
    111
    The hidden GameObject can stick around sometimes in an uninitialized state, causing all manner of grief. So I changed EnsureHelper() to this. Lines 9-12 are changed/added. Also made it not hidden.

    Code (CSharp):
    1.     public static void EnsureHelper()
    2.     {
    3.         lock (syncRoot)
    4.         {
    5. #if !NO_UNITY
    6.             if (null == (object)instance)
    7.             {
    8.                 instance = FindObjectOfType(typeof(UnityThreadHelper)) as UnityThreadHelper;
    9.                 if (null == (object)instance || null == (object)instance.dispatcher)
    10.                 {
    11.                     if(null == (object)instance.dispatcher)
    12.                         Destroy(instance);
    13.  
    14.                     var go = new GameObject("[UnityThreadHelper]");
    15.                     go.hideFlags = HideFlags.NotEditable; // | HideFlags.HideInHierarchy | HideFlags.HideInInspector;
    16.                     instance = go.AddComponent<UnityThreadHelper>();
    17.                     instance.EnsureHelperInstance();
    18.                 }
    19.             }
    20. #else
    21.             if (null == instance)
    22.             {
    23.                 instance = new UnityThreadHelper();
    24.                 instance.EnsureHelperInstance();
    25.             }
    26. #endif
    27.         }
    28.     }
    29.  
     
    Last edited: Oct 4, 2014
  31. misterPantoni

    misterPantoni

    Joined:
    Feb 7, 2013
    Posts:
    44
    Hey there,

    i am currently resarching some threading-stuff in unity and found your package - first of all really nice, thanks a lot for sharing it here...
    I have some (somehow) general Questions about Multithreading/Tasks:

    Currently i am trying to implement your Task System to my Pathfinding, which works good - except i really try to push it and calculate a lot of paths at one time, then i get a "Error while processing task - null ref" exception. So my question is: Am i doing this right or do i have a complete misunderstanding of threads and your scripts at all?

    Whenever i call my "GetPath" function, i start a Task
    Code (CSharp):
    1.  
    2. UnityThreading.Task task = UnityThreadHelper.TaskDistributor.Dispatch(()=>{
    3.             Search();
    4.         });
    5.  
    ) to do the search. Then i subscribe to the "TaskEnded" Event (
    Code (CSharp):
    1. task.TaskEnded += TaskEnded;
    ).
    I don't need more than one path to be calculated at the same time,so my "Pathfinder" class has a bool ("searching") which is set to true when i start the task and is set to false when the task is ended.

    When i found my Path i Dispatch the Result:
    Code (CSharp):
    1.             UnityThreadHelper.Dispatcher.Dispatch(()=>{
    2.                 FoundPath(currentNode);
    3.             });
    As i already mentioned, this works if i do not try to calc a lot of paths at the same time, but i need something that is really stable... ANY ideas on what i am doing wrong - where to investigate further?
     
  32. misterPantoni

    misterPantoni

    Joined:
    Feb 7, 2013
    Posts:
    44
    I know this is an old thread, but is there no one who can bring some light into my multithreading-darkness?
     
  33. ZJP

    ZJP

    Joined:
    Jan 22, 2010
    Posts:
    2,649
    Dead product? :(
     
  34. creat327

    creat327

    Joined:
    Mar 19, 2009
    Posts:
    1,626
    did anyone managed to get this to work on windows phone or windows 8.1 ?
     
  35. ZammyIsOnFire

    ZammyIsOnFire

    Joined:
    Jul 7, 2014
    Posts:
    9
    I am trying to use UnityThreadHelper.Dispatcher to delegate a repaint of a EditorWindow.

    I am doing UnityThreadHelper.EnsureHelper() in OnEnable of said EditorWindow.

    The problem is that UnityThreadHelper.Dispatcher is null.

    Does this library work in Editor?
     
  36. FlySoFast

    FlySoFast

    Joined:
    Feb 10, 2015
    Posts:
    1
    Thank you. I couldn't make use of the Dispatcher because I was calling it directly from a non-UI thread at first call. I was about to give up on this awesome work because I'm not good enough to understand all the code and see how it works. You did the dirty job for us, like a Batman.

    @Marrrk I can't thank you enough!! This is exactly what I've been looking for! Worked perfectly!
     
  37. Jake-L

    Jake-L

    Joined:
    Oct 17, 2009
    Posts:
    397
    I got some issues using the TaskDistributor: I basically want to add 20 tasks and wait until they're done:

    Edit: I stripped down my problem and added better example code:

    Code (csharp):
    1.  
    2. UnityThreading.Task[] tasks = new UnityThreading.Task[20];
    3. // feed tasks
    4. for (int i = 0; i < 20; i++)
    5.    tasks[i]=UnityThreadHelper.TaskDistributor.Dispatch( () =>
    6. {
    7.     for (int x = 0; x < 1000; x++)
    8.     {
    9.           Vector3 v = new Vector3(1, 2, 3);
    10.           v.Normalize();
    11.     }
    12. }
    13. );
    14. // wait until all are done        
    15. for (int i = 0; i < 20; i++)
    16.     tasks[i].Wait();
    17. // proceed...
    18.  
    If I remove threading from the above code, each of the 20 runs need about 1.6 millisecs. If I run the above code, ~15 runs take 1.8ms (ok, there's overhead), but ~5 runs take over 180ms (!).

    Tested in the editor and windows standalone. No matter how much work I add in, a few tasks cause very large delays.

    Does anyone have an idea how to fix this?

    Jake
     
    Last edited: May 29, 2015
  38. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    Try limit the number of tasks to the number of cores you have in your cpu ;)
     
  39. Jake-L

    Jake-L

    Joined:
    Oct 17, 2009
    Posts:
    397
    That's a good idea, but that didn't solved it either:

    If I create a TaskDistributor with 1 single thread, no spikes occur.
    With a TaskDistributor with 2 threads or more I got spikes again.

    Running on a Core i7 (4 Cores, 8 Logical Processors). The example code above is quite minimalistic, but an execution time ranging from 0.6ms to >100ms isn't reliable at all.
     
  40. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    You should not use threads on a so simple piece of code, to be revelent, the test should be run over 1000000 iterations at least.
    Also note that the threads initialization takes a little time, so you should use them when you're sure it's worth doing so.
     
  41. Jake-L

    Jake-L

    Joined:
    Oct 17, 2009
    Posts:
    397
    A calculation that takes 2ms on my machine might use 20ms on the next mobile CPU running my code. Also, this was just example code. Even if I use a bigger loop, the problem still exists (40ms vs 200ms spikes).

    I thought that the TaskDistributor is exactly for that purpose as it creates worker threads just once and then just executes code in them when I call Dispatch(). Am I wrong with this?
     
  42. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    I'm not sure about this, sorry :/

    Anyway, you could try something else :

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Threading;
    4.  
    5. public static class ThreadsManager {
    6.  
    7.     private struct ThreadPoolData
    8.     {
    9.         public Action action;
    10.         public ManualResetEvent doneEvent;
    11.     }
    12.    
    13.     private static ManualResetEvent[] doneEvents = new ManualResetEvent[1];
    14.    
    15.     public static void Pool(params Action[] actions){
    16.  
    17.         // update events list length
    18.         if(doneEvents.Length < actions.Length){
    19.             doneEvents = new ManualResetEvent[actions.Length];
    20.         }
    21.  
    22.         // reset events in the list
    23.         for (int i = 0; i < doneEvents.Length; i++){
    24.             if(doneEvents[i] == null){
    25.                 doneEvents[i] = new ManualResetEvent (true);
    26.             }else{
    27.                 doneEvents[i].Set();
    28.             }
    29.         }
    30.  
    31.         // execute actions
    32.         for (int i = 0; i < actions.Length; i++){
    33.             doneEvents[i].Reset();
    34.            
    35.             ThreadPoolData data = new ThreadPoolData();
    36.             data.action = actions[i];
    37.             data.doneEvent = doneEvents[i];
    38.            
    39.             System.Threading.ThreadPool.QueueUserWorkItem (new System.Threading.WaitCallback (PoolNewThread), data);
    40.         }
    41.  
    42.         // wait for all actions
    43.         WaitHandle.WaitAll (doneEvents);
    44.     }
    45.    
    46.     private static void PoolNewThread(System.Object stateInfo){
    47.         ThreadPoolData data = (ThreadPoolData)stateInfo;
    48.         data.action.Invoke ();
    49.         data.doneEvent.Set();
    50.     }
    51.  
    52. }
    53.  
    and use it like this :
    Code (CSharp):
    1.  
    2. double now = Time.realtimeSinceStartup;
    3. ThreadsManager.Pool(() => yourFunction1() , () => yourFunction2() , etc...);
    4. now = Time.realtimeSinceStartup - now;
    5. Debug.Log("elapsed time = "+(now*1000).ToString("f3")+"ms");
     
  43. Jake-L

    Jake-L

    Joined:
    Oct 17, 2009
    Posts:
    397
    Thanks for this example, I'll look into it. Though it looks like reinventing the wheel as this kind of management is exactly what UTH should do.
     
  44. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    I was working with UTH too, but since I needed something simpler, I used this instead
     
  45. prestonmatterport

    prestonmatterport

    Joined:
    May 7, 2015
    Posts:
    28
    Any way you can #!UNITY_WEBGL the threading code? Maybe have Dispatch() just run the action synchronously? It will be increasingly helpful to people as WebGL becomes more popular.
     
  46. Cripple

    Cripple

    Joined:
    Aug 16, 2012
    Posts:
    92
    TaskDistributor.TaskCount reports 0 tasks while there are active tasks. IsWorking is also set to false.

    Does it work for you ?
     
    Last edited: Sep 6, 2015
  47. DarkMasster

    DarkMasster

    Joined:
    Mar 23, 2015
    Posts:
    14
    Hi All *)))

    Any one help me please ! I need do some "work" in a sequance? step by step. How can i use UnityThreadHelper for solve this ?
     
  48. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    Threads are useful for jobs in parallel, not in sequence...

    What you can do, is to try to split each step in chunks and distribute them over threads, to reduce the execution time of this step.

    For example, if you have 4 threads and to do a loop on an array with 40000 entries, you can split this process in 4 and use one thread to deal with entries from 0 to 9999, then another for 100000 to 19999, the next for 20000 to 29999, and the fourth for 30000 to 39999.

    This is a very basic example, but this is the main idea.
     
  49. Delaley

    Delaley

    Joined:
    Jan 19, 2016
    Posts:
    6
    Please Anyone with a working Example to LoadTextures without blocking the main Thread?
     
  50. Juechen

    Juechen

    Joined:
    Apr 8, 2016
    Posts:
    3
    I have almost the same problem. I want to load a large amount of textures in the background while showing these textures in the main thread. Can someone show me how to do this? Thanks in advance. @Marrrk, @Alesk

    For example, I have 1000 images in a folder. When I click a button on the screen, I want to create a back groud task to load these images, and at the same time the main thread play these images one by one. Forgive my poor English :)
     
    Last edited: Apr 8, 2016