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

    Saticmotion_legacy

    Joined:
    May 14, 2011
    Posts:
    24
    I've been playing around with this for a bit, but Unity is constantly locking itself, and I don't know why. I've probably missed something important, but I can't see it.

    I've split up a loop of 100*100 iterations into 4 loops of 25*100 iterations. In here I'm instantiating Transforms.

    The relevant code:

    Code (csharp):
    1. void Start ()
    2.     {      
    3.         var thread1 = UnityThreadHelper.TaskDistributor.Dispatch(() => InstantiateCubes(gridX / 4 * 0, gridX / 4 * (0 + 1)));
    4.         var thread2 = UnityThreadHelper.TaskDistributor.Dispatch(() => InstantiateCubes(gridX / 4 * 1, gridX / 4 * (1 + 1)));
    5.         var thread3 = UnityThreadHelper.TaskDistributor.Dispatch(() => InstantiateCubes(gridX / 4 * 2, gridX / 4 * (2 + 1)));
    6.         var thread4 = UnityThreadHelper.TaskDistributor.Dispatch(() => InstantiateCubes(gridX / 4 * 3, gridX / 4 * (3 + 1)));
    7.        
    8.         thread1.Wait();
    9.         thread2.Wait();
    10.         thread3.Wait();
    11.         thread4.Wait();
    12.     }
    And:

    Code (csharp):
    1. private void InstantiateCubes(int lowerLimit, int upperLimit)
    2.     {
    3.        
    4.         System.Random rand = new System.Random();
    5.        
    6.         for (int x = lowerLimit ; x < upperLimit; x++)
    7.             {
    8.                     for (int y = 0; y < gridY; y++)
    9.                     {
    10.                         int j = rand.Next(0,2);
    11.                         if (j == 0)
    12.                             grid[x,y] = false;
    13.                         else
    14.                         grid[x,y] = true;
    15.                            
    16.                         cubeGrid[x, y] = Instantiate(cubePrefab, new Vector3(1.4f * x + 0.5f, 1f, 1.4f * y + 0.5f), Quaternion.identity) as Transform;     
    17.                         cubeGrid[x, y].renderer.enabled = false;
    18.                     }
    19.             }
    20.     }
     
  2. lilymontoute

    lilymontoute

    Joined:
    Feb 8, 2011
    Posts:
    1,181
    I believe that Instantiate must be called on the main thread. Most of Unity's API isn't thread safe at all. I would suggest creating Vector3 arrays for your positions on the worker threads, then doing the actual instantiation afterwards on the main thread.
     
  3. Saticmotion_legacy

    Saticmotion_legacy

    Joined:
    May 14, 2011
    Posts:
    24
    I've read it through again, and apparently what I'm doing there is what the thread should send to the main tread. So I'm doing it wrong :)

    Forget that^. What I want to do is just impossible.

    That's the third way of parallellisation that fails to process my logic.
     
    Last edited: Feb 15, 2012
  4. TouchSoft

    TouchSoft

    Joined:
    Oct 18, 2008
    Posts:
    218
    Does the iPhone / iPad support multithreading? If not, does this system have fall backs to single thread?
     
  5. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    @Thinksquirrel: Saticmotion is right, you need to call Unity API methods inside the main thread, thats why the Dispatcher class exist, look at the sample code provided to see how to do this. For your code, I dont see any benefits from this though. CAlculating positions should not be that hard to do in a ssingle thread environment.

    @TouchSoft: iOS systems are multithreaded and it should work there, only consoles may have some problems (Wii for example). There is no single threaded fallback system.
     
  6. Saticmotion_legacy

    Saticmotion_legacy

    Joined:
    May 14, 2011
    Posts:
    24
    Thanks for the reply!
    I actually askedthis same thing here where I do my internship, and they told me it's slow because of the editor. We made a build, and the load time of 30sec+ became half a second of loading :)
     
  7. Wink00

    Wink00

    Joined:
    Aug 22, 2010
    Posts:
    46
    I am pretty new to multithreading... not to mention multithreading with unity.
    So, what i would like to do is create one procedural texture (perlin noise type) with the main thread, then create other textures with background threads.
    How would be the best way to go about this?
    If i hold these textures in a 2D array [2,2] for example, do i need to lock the array when reading and writing to it? Could i not lock it if i ONLY read from it?
    What would be the proper method to call for doing this? Create or Dispatch?

    Thanks for any much needed help!! :))

    EDIT: I have this figured out, thanks. :) Just don't call Unity libraries with a background thread...haha. :p
     
    Last edited: Feb 21, 2012
  8. immFX

    immFX

    Joined:
    Mar 20, 2010
    Posts:
    110
    Am I missing something here? Using it like you said in UnityScript, I get ambiguous reference error:

    Ambiguous reference 'CreateThread': UnityThreadHelper.CreateThread(function(): void), UnityThreadHelper.CreateThread(function(UnityThreading.ActionThread): void).


    EDIT: found out the problem, my Thread function called Unity API methods, that's why it failed.

    My apologies to Marrrk (and his awesome by all means contribution), but my initial enthusiasm has faded. I mean what's the point to create extra threads for some arithmetic calculations or similar, it looks like (pls. correct me if I'm wrong) that the overhead to create/remove thread(s) is larger than keeping these computations in the Main thread.
     
    Last edited: Feb 20, 2012
  9. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Hi immFX:

    small calculations which are fast enough to be used in the mainthread and even only from time to time are indeed not very usefull to be called in an backgroundthread.

    But how about complexer simulations, simulations which will take, say 100ms or even longer to complete. Using these in the mainthread will block the game or make the game stutter. also why would you want to waste a big fraction of the CPU power you may be able to utilize :)

    For this I created this little helper classes:

    You want to calculate the AI? Put it in a long running Thread (CreateThread).
    You want a complex action to be calculated, like destroying a mesh or processing some data like an image? Put it in a Task (TaskDistributor)

    You should never create a new thread only for single actions, thats what the TaskDistributor is for. The TaskDistributor will create a task and runs it inside of one of the many precreated TaskDistributor background threads. These threads are already created and spawning a new task is much faster than creating a new thread.

    tl/dr:

    Single operations which are very fast and you need the result immediately: Run this operation on the current thread.
    Single operations which may take some time or which result is not needed immediately: Use the TaskDistributor.
    Many operations or repeating operations who only need to notify me when something newsworthy happened: Use a own thread.
    Need to send something to an other thread: Use the Dispatcher.


    Here some example for the usage of an thread:

    Code (csharp):
    1.  
    2. UnityThreading.ActionThread aiThread;
    3. List<AiComponent> registeredAis = new List<AiComponent>();
    4. AutoResetEvent registeredAisChangedEvent = new AutoResetEvent(false);
    5.  
    6. void Start()
    7. {
    8.     aiThread = UnityThreadHelper.CreateThread(DoAiThreadWork);
    9. }
    10.  
    11. public void RegisterAi(AiComponent ai)
    12. {
    13.     lock (workingAis)
    14.     {
    15.         workingAis.Add(ai);
    16.         registeredAisChangedEvent.Set();
    17.     }
    18. }
    19.  
    20. public void UnregisterAi(AiComponent ai)
    21. {
    22.     lock (workingAis)
    23.     {
    24.         workingAis.Remove(ai);
    25.         registeredAisChangedEvent.Set();
    26.     }
    27. }
    28.  
    29. void DoAiThreadWork(UnityThreading.ActionThread thread)
    30. {
    31.     List<AiComponent> registeredAisCopy;
    32.     lock (registeredAis)
    33.     {
    34.         registeredAisCopy = new List<AiComponent>(registeredAis);
    35.     }
    36.        
    37.     while (!thread.ShouldStop))
    38.     {
    39.         if (registeredAisChangedEvent.WaitOne())
    40.         {
    41.             lock (registeredAis)
    42.                 registeredAisCopy = new List<AiComponent>(registeredAis);
    43.         }
    44.        
    45.         foreach (var currentAi in registeredAisCopy)
    46.         {
    47.             currentAi.Process();
    48.         }
    49.     }
    50. }
    51.  
    Only quickly written.
     
    Last edited: Feb 22, 2012
  10. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    Hi !

    Back to threads work, and I've got a problem ^^

    This code is working :
    Code (csharp):
    1.  
    2. Vector3 pos = pCameraTransform.position;
    3. Matrix4x4 mtr = pLight.transform.worldToLocalMatrix;
    4. int _count = SystemInfo.processorCount;
    5.  
    6. var task1 = UnityThreadHelper.TaskDistributor.Dispatch(() => SortTask(0,_count,pos,mtr));
    7. var task2 = UnityThreadHelper.TaskDistributor.Dispatch(() => SortTask(1,_count,pos,mtr));
    8. var task3 = UnityThreadHelper.TaskDistributor.Dispatch(() => SortTask(2,_count,pos,mtr));
    9. var task4 = UnityThreadHelper.TaskDistributor.Dispatch(() => SortTask(3,_count,pos,mtr));
    10.            
    11. task1.Wait();
    12. task2.Wait();
    13. task3.Wait();
    14. task4.Wait();
    15.  
    And this one is freezing unity :
    Code (csharp):
    1.  
    2. Vector3 pos = pCameraTransform.position;
    3. Matrix4x4 mtr = pLight.transform.worldToLocalMatrix;
    4. int _count = SystemInfo.processorCount;
    5. int i = 0;
    6. List<UnityThreading.Task> taskList = new List<UnityThreading.Task>();
    7.            
    8. for(i=0;i<_count;i++){
    9.     taskList.Add(UnityThreadHelper.TaskDistributor.Dispatch(() => SortTask(i,_count,pos,mtr)));
    10. }
    11.            
    12. for(i=0;i<_count;i++){
    13.     taskList[i].Wait();
    14. }
    15.  
    16. taskList.Clear();
    17.  
    Please could someone explain where I am wrong ?
     
  11. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Well if its more than that, I need to have a look into the code but try this:

    Code (csharp):
    1.  
    2. Vector3 pos = pCameraTransform.position;
    3. Matrix4x4 mtr = pLight.transform.worldToLocalMatrix;
    4. int _count = SystemInfo.processorCount;
    5. int i = 0;
    6. List<UnityThreading.Task> taskList = new List<UnityThreading.Task>();
    7.            
    8. for(i=0;i<_count;i++){
    9.     var current = i;
    10.     taskList.Add(UnityThreadHelper.TaskDistributor.Dispatch(() => SortTask(current,_count,pos,mtr)));
    11. }
    12.            
    13. for(i=0;i<_count;i++){
    14.     taskList[i].Wait();
    15. }
    16.  
    17. taskList.Clear();
    18.  
     
  12. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    Bingo, that's working now ! Thank you so much, I hate being stuck on stupid things like this ^^

    I have a second problem : I'm using this to Z sort particles, since I can't sort the same array with multiple threads, I'm doing this with multiple sub arrays, sort them individualy in each thread, and then merge them back in a single loop to reconstruct my particles back to front, each time by picking the farthest index in each sorted sub array.

    If I don't use the threading method, and "simulate" this by doing 4 calls to my SortTask function, everything is fine. But if I use the threads, something is messed up in my particles and some of them are poping randomly, but only if I render many of them.
    You can see this bug here : http://www.alesk.fr/demo/smoke/pop-bug/
    And you can see what it should looks like here : http://www.alesk.fr/demo/smoke/no-threads/
    where I've turned off the threads, but the sorting is still done in multiple steps.

    I'm preparing some cleaner code to post it here, but in the meantime, do you have any guess of where this bug could be ?
     
  13. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    My guess would be that the sort methods are fiddling with each others data (race condition). I am looking forward to see your code.
     
  14. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    Ok, that's weird since each sub group is sorting a different part of the main list.
    Anyway, I should have my sample code ready for this evening
    Thanks for your help ;)
     
  15. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    Ok... so the bug doesn't comes from the sorting threads, but from another part of the code... I'm trying to isolate it...

    [EDIT] bug found ! It seems that some variables were mixed between threads, I don't know how, but I solved my problem by merging multiple lines into one, and removing some variables.

    Now my particle are properly sorted and displayed :)
    http://www.alesk.fr/demo/smoke/v2/
     
    Last edited: Feb 27, 2012
  16. Jay_Santos

    Jay_Santos

    Joined:
    May 31, 2011
    Posts:
    42
    Quick question, I am doing the following:

    Code (csharp):
    1. UnityThreading.ActionThread myThread;
    2.  
    3. void Start()
    4. {
    5.   myThread = UnityThreadHelper.CreateThread(DoThreadWork);
    6. }
    7.  
    8. void DoThreadWork()
    9. {
    10.   while (true)
    11.   {
    12.       debug.log("Inside the thread...");
    13.       //sleep. I can't remember the exact command I used for sleep, but it was working, the thread was sleeping for the given time
    14.   }
    15. }
    16.  
    17.     void OnApplicationQuit()
    18.     {
    19.         myThread.destroyImmediate();
    20.     }
    However, on Editor, whenever I quitted the game the "Inside the thread" message was still being printed... I haven't tested on devices, even thought I am explicitly calling destroyImmediate in the OnApplicationQuit method.

    Is that an issue with Editor? Should I ignore it?

    Thanks for your work!
     
  17. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Hi there, in fact this is a bug, a bug which only occours in the Play to Edit mode transition, I fixed this at the added zip file, which contains some new features which may be usefull for some people.

    Also you should not call myThread.destroyImmediate(), my Thread class does not contain this method ;)

    To close a Thread you can call Abort(), AbortWaitForSeconds(...) or Dispose(), the Dispose method will terminate the thread after 1 second, so in most cases you want to use one of the Abort methods. But as you created the Thread through UnityThreadHelper.CreateThread(...) the thread will be automatically closed at application exit/level change.

    So.. new feature:

    All classes who are based upon the DispatcherBase class, which will be the Dispatcher and the TaskDistributor, are now able to prioritize the Tasks. How is this usefull? Say you have something like a loading system for rectangular levels based upon the player position, everything near the player should be loaded before everything more far away. You can set the priority for the loading of near levels to a very high value and the priority for far away levels to a lower value, the tasks with a low value will be executed first.

    How to use it? First you need to setup the DispatcherBase based classes. Do this by setting the TaskSortingSystem value of an instance to the desired value:

    TaskSortingSystem.NeverReorder
    This is the default value, priorities will ignored.

    TaskSortingSystem.ReorderWhenAdded
    When a new Task has been added the current Task list will be reordered.

    TaskSortingSystem.ReorderWhenExecuted
    When a new Task has been added or an existing task has been executed the current Task list will be reordered.

    Every Task has now a Priority member which can be changed whenever you want, based upon the TaskSortingSystem value of a DispatcherBase based class the value will be evaluated.

    A last change affects the TaskBase class (every Task inherits from this class), you can now inherit from this class with less setup needed, just inherit from it and implement the Do() method. Coming eith this change: All DispatcherBase based classes now contain a new method: Dispatch(TaskBase task).

    Let me know if you or any other have special needs, regarding the UnityThreading helper stuff.

    View attachment $UnityThreading.zip
     
  18. lilymontoute

    lilymontoute

    Joined:
    Feb 8, 2011
    Posts:
    1,181
    Hey Marrrk,

    Just wanted to let you know that I've been able to add support for micro mscorlib (for iPhone stripping). I'm working on a forked version (plus github is acting a bit wonky atm so it's not posting up yet), so I don't have your most recent version integrated with that.

    All I did was change System.Environment.ProcessorCount to UnityEngine.SystemInfo.processorCount, and WaitOne(0) to WaitOne(0, false). I figured you may want to incorporate that into your package.
     
  19. sukisan

    sukisan

    Joined:
    Nov 7, 2011
    Posts:
    3
    Hey, great package, thanks for sharing!

    My first usage of the UnityThreadHelper is from a thread other than the main thread (destructor of a helper object). The first call to UnityThreadHelper.Dispatcher implicitly creates the singleton instance, which in turn uses FindObjectOfType, which is only allowed in the main thread and crashed.

    I had to work around by ensuring the singleton is correctly instantiated in by doing something like this in another MB:

    Code (csharp):
    1.     void Start() {
    2.         var ugly = UnityThreadHelper.Dispatcher;    // ugly, but necessary - singleton instatiation
    3.  
    Nothing someone dies of, but maybe you find a nicer way to ensure the singleton and everything is set up properly
     
    FlySoFast and ZammyIsOnFire like this.
  20. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    @Thinksquirrel: I see, well I will integrate your changes into the version I provide here, when I find the time for this :D

    @sukisan: I can change the UnityThreadHelper Component to be usable without the "singleton" pattern it currently uses. So that the user can add the component himself to a gameobject, or simply creating a prefab with the UnityThreadHelper, I think that would be more flexible, as it gives the ability to control the lifetime of the component (currently it will be destroyed when changing scenes/levels).
     
    ZammyIsOnFire likes this.
  21. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Hi there, I added a new version with some nice new features:

    - TaskBase.Do returns now an IEnumerator
    - ThreadBase.Do returns now an IEnumerator
    - changed System.Environment.ProcessorCount to UnityEngine.SystemInfo.processorCount, and WaitOne(0) to WaitOne(0, false)
    - UnityThreadHelper has now a new static method called EnsureHelper(), call this somewhere at startup time of your application, its basically the same thing as var ugly = UnityThreadHelper.Dispatcher; without the ugly part

    To the both main changes (IEnumerator return value):

    This may be a breaker for you, when you inherited manually from one of the both base classes (which I encourage to do) you will need to change the overloaded methods return values to IEnumerator too. Simply changing the return value and adding a return null; should ensure compatibility. When you did not inherting from one of these class you have nothing to worry about.

    Why I did this change? Simply to allow yield instructions inside of Tasks and Threads, it works similar to the yield System of Unity with the difference that everything you yield should be based upon the TaskBase class.

    A little example:
    Code (csharp):
    1.  
    2. IEnumerator Do()
    3. {
    4.   DoSomethingFancy();
    5.   yield return new MyTask();
    6.   DoSomethingOther();
    7. }
    8.  
    The new instance of MyTask (which inherits from TaskBase) will be executed on the main thread and the Do method will wait for completion of the this newly created task.

    This may look strange at the first sight, but I hope to add some new functionality based upon this: http://forum.unity3d.com/threads/90128-Unity-Threading-Helper?p=585586&viewfull=1#post585586

    Here the new version:

    View attachment $UnityThreading.zip

    I should really put this into the asset store :/
     
    Last edited: Mar 22, 2012
  22. Jay_Santos

    Jay_Santos

    Joined:
    May 31, 2011
    Posts:
    42
    You should, and I make sure to buy it even though I am using it already. Great job!
     
  23. lilymontoute

    lilymontoute

    Joined:
    Feb 8, 2011
    Posts:
    1,181
    So the irony of this - It ends up breaking Web Player support (throws a SecurityException) when using WaitOne(0, false).

    So we're in a situation where WaitOne(0) won't compile with micro mscorlib, but WaitOne(0, false) throws an exception in the web player. using defines works perfectly here, and for most people that is fine (you may want to include that), except that I'm using the library in an assembly. I have a workaround I'm about to try out (still on the old fork) that invokes WaitOne(0) dynamically on the web player, to escape the compile errors on iOS. It's very, very ugly though.

    I'll let you know how that goes, and send you the relevant code if you're interested.

    Edit: Success! Got it to work on both the Web Player and iOS with full stripping. You can check out the forked code here - https://github.com/Thinksquirrel-So...Plugins/Thinksquirrel Common/Source/Threading

    Also, I tried to upgrade the libraries earlier (to your most recent one with the IEnumerator changes) and it seemed to break on iOS. Ended up reverting back to the version before it. Any idea why that may be the case? I call threads like this (pseudocode):

    Code (csharp):
    1.  
    2. CreateThread( () =>
    3.     {
    4.  
    5.         // Some work...
    6.  
    7.         UnityThreadHelper.Dispatcher.Dispatch( () => /* a callback function */ );
    8.     }
    9.  
    I only did some short tests (the project is near shipping, so I didn't devote a lot of time to it), but the callback wasn't getting called at all, and I'm not sure if work was being done either. Is there a better way to do asynchronous callbacks?
     
    Last edited: Mar 30, 2012
  24. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    I did a small test (based upon the zip I attached here somewhere) which consisted of this inside the Start method in a small MonoBehaviour:

    Code (csharp):
    1.  
    2. UnityThreadHelper.CreateThread(() =>
    3.             {
    4.                 UnityThreadHelper.Dispatcher.Dispatch( () => UnityEngine.Debug.Log("Wheee") );
    5.             });
    6.  
    Worked as expected. "Wheee" was successfully printed.
     
  25. lilymontoute

    lilymontoute

    Joined:
    Feb 8, 2011
    Posts:
    1,181
    Hm, it's likely something on my end preventing the callback then. I'm passing the callback as a System.Action argument to a method (I tried dispatching it directly and wrapping it in another action also). I'll have to double check it, though.
     
  26. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    Hi,

    I've got a speed problem with a very basic thread setup :

    Code (csharp):
    1. using UnityEngine;
    2. using System.Threading;
    3. using System.Collections;
    4.  
    5. public class bench : MonoBehaviour {
    6.    
    7.     private int CoresCount = 1;
    8.     private double ThreadTime = 0;
    9.     private int threadFinishedCount = 0;
    10.    
    11.     void Start () {
    12.         CoresCount = SystemInfo.processorCount;
    13.     }
    14.    
    15.     void Update () {
    16.         TestThread();
    17.     }
    18.    
    19.     void OnGUI() {
    20.         GUILayout.Label("Thread Time "+(ThreadTime*1000).ToString("f2")+"ms ("+CoresCount+" threads)");
    21.     }
    22.    
    23.     private void TestThread(){
    24.         ThreadTime = Time.realtimeSinceStartup;
    25.  
    26.         threadFinishedCount = 0;
    27.        
    28.         for(int i=0; i < CoresCount; i++){         
    29.             new Thread(ThreadFunction).Start();
    30.         }
    31.        
    32.         while (threadFinishedCount < CoresCount){
    33.         }
    34.            
    35.         ThreadTime = Time.realtimeSinceStartup - ThreadTime;
    36.     }
    37.  
    38.     public void ThreadFunction(){
    39.         threadFinishedCount++;
    40.     }
    41.    
    42. }
    With this I get around 2 ms of execution time, where I get almost 0 using the threading helper.
    Please could you tell me where I am wrong ?
     
  27. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    I am not even sure what you are comparing this against, could you show me the code you use with the threading helper?

    But just from looking over this code: You spawn up to CoreCount threads and wait for completion, every thread start takes up some time, which may be your 2ms.
     
  28. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    Yeah thank you. I've found that recreating the threads in each loop was using these 2ms
    By storing them in an array and recycling them, I'm back to near 0ms, but I've still a little problem.
    This is my current version, trying to simplify you threading helper for my purpose, and it's still slower that yours :

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System;
    4. using System.Threading;
    5. using System.Collections;
    6. using System.Collections.Generic;
    7.  
    8. public class bench : MonoBehaviour {
    9.    
    10.     private int CoresCount = 1;
    11.     private double ThreadTime = 0;
    12.     private double ThreadTime2 = 0;
    13.    
    14.     private customThread[] ThreadList;
    15.    
    16.     void Start () {
    17.         CoresCount = SystemInfo.processorCount;
    18.         ThreadList = new customThread[CoresCount];
    19.        
    20.         for(int i=0; i<CoresCount; i++){
    21.             ThreadList[i] = new customThread();
    22.         }
    23.        
    24.     }
    25.    
    26.     void Update () {
    27.         TestThread();
    28.     }
    29.    
    30.     void OnGUI() {
    31.         GUILayout.Label("Thread Time Alesk : "+(ThreadTime*1000).ToString("f2")+"ms ("+CoresCount+" threads)");
    32.         GUILayout.Label("Thread Time Marrrk : "+(ThreadTime2*1000).ToString("f2")+"ms ("+CoresCount+" threads)");
    33.     }
    34.    
    35.     private void TestThread(){
    36.         // my crappy attempt to get something
    37.         ThreadTime = Time.realtimeSinceStartup;
    38.        
    39.         int i;
    40.         for(i=0; i < CoresCount; i++){
    41.             var j=i;
    42.             ThreadList[i].function = () => ThreadFunction("testA",j);
    43.             ThreadList[i].Start();
    44.         }
    45.        
    46.         while(customThread.count>0); // wait for all threads to finish
    47.        
    48.         ThreadTime = Time.realtimeSinceStartup - ThreadTime;
    49.        
    50.        
    51.        
    52.         // reference time using Threading Helper
    53.         ThreadTime2 = Time.realtimeSinceStartup;
    54.        
    55.         List<UnityThreading.Task> taskList = new List<UnityThreading.Task>();
    56.         for(i=0;i<CoresCount;i++){
    57.             var j = i;
    58.             taskList.Add(UnityThreadHelper.TaskDistributor.Dispatch(() => ThreadFunction("testB",j))); 
    59.         }
    60.         for(i=0;i<CoresCount;i++){
    61.             taskList[i].Wait();
    62.         }
    63.         taskList.Clear();
    64.        
    65.         ThreadTime2 = Time.realtimeSinceStartup - ThreadTime2;
    66.        
    67.     }
    68.  
    69.     public void ThreadFunction(string stringVar, int integerVar){
    70.         // do something big here
    71.         Debug.Log(stringVar+" "+integerVar);
    72.     }
    73.        
    74.     private class customThread{
    75.         public static int count = 0;
    76.        
    77.         public Action function;
    78.         private Thread myThread;
    79.        
    80.         public customThread(){
    81.             myThread = new Thread(this.doFunction);
    82.         }
    83.        
    84.         private void doFunction(){
    85.             function();
    86.             count--; // function is finished, decrease the running thread counter
    87.         }
    88.        
    89.         public void Start(){
    90.             count++; // new thread is started, increase the thread counter
    91.             doFunction();
    92.         }
    93.     }
    94. }
    95.  
    It looks like with my solution, the functions are not executed in parallel, that's why it's slower... please could you tell what should I change ?
     
  29. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    You dont start your threads, instead of that you just run the function the thread should execute.

    From the MSDN documentation to the Thread class you use:
    By that the Start methof of the Thread class is meant not the Start method of your customThread class ;)
     
  30. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    How stupid am I ! -__-

    Thanks for this... but now I have another problem : I can't invoke the Start() Method more than once on each Thread, I can't use the Abort() method inside the my doFunction() methode, this causes an error.

    How should I do to free the threads state once used, without having to recreate them for the next loop ?
     
  31. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    The UnityThreadingHelper TaskDistributor sends, lets say, Functions/Actions to the free threads. Those threads never end after the execution of one of these. So I call Start only once:

    Pseudocode:
    Code (csharp):
    1.  
    2. DoThread()
    3. {
    4.   while (true)
    5.   {
    6.     eventId = waitForEvent(new Events[] { exitThreadEvent, newActionEvent });
    7.     if (eventId == 0)
    8.     {
    9.       return;
    10.     }
    11.     thingToDo = getNewAction();
    12.     thingToDo.Do();
    13.   }
    14. }
    15.  
    Edit: Just curious, why are you doing that?
     
    Last edited: Apr 10, 2012
  32. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    Thanks for this reply :)
    I'm doing that to get a simpler thread management embeded into my class
     
  33. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Is the UTH to big for your purposes?
     
  34. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    Nope, it's great as it is :)
    I just like to do it by understanding everything myself
     
  35. Alesk

    Alesk

    Joined:
    Jul 15, 2010
    Posts:
    337
    Here is a new experiment using the ThreadPool class.
    This is still slower than your UTH, and crashes my main project after some time... don't know why... But since I don't have more time to spend on this, I'll stick with UTH anyway ;)

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System;
    4. using System.Threading;
    5. using System.Collections;
    6. using System.Collections.Generic;
    7.  
    8. public class bench : MonoBehaviour {
    9.    
    10.     private int CoresCount = 1;
    11.     private double ThreadTime = 0;
    12.     private double ThreadTime2 = 0;
    13.    
    14.     void Start () {
    15.         CoresCount = SystemInfo.processorCount;
    16.     }
    17.    
    18.     void Update () {
    19.         TestThread();
    20.     }
    21.    
    22.     void OnGUI() {
    23.         GUILayout.Label("Thread Time Alesk : "+(ThreadTime*1000).ToString("f2")+"ms ("+CoresCount+" threads)");
    24.         GUILayout.Label("Thread Time Marrrk : "+(ThreadTime2*1000).ToString("f2")+"ms ("+CoresCount+" threads)");
    25.     }
    26.    
    27.     private void TestThread(){
    28.         // my crappy attempt to get something
    29.         ThreadTime = Time.realtimeSinceStartup;
    30.  
    31.         int i;
    32.         for(i=0; i < CoresCount; i++){
    33.             var j=i;
    34.             threadsManager.StartThread(() => ThreadFunction("testA1",j));
    35.         }
    36.         threadsManager.Wait(); // wait for all threads to finish
    37.         ThreadTime = Time.realtimeSinceStartup - ThreadTime;
    38.  
    39.         // reference time using Threading Helper
    40.         ThreadTime2 = Time.realtimeSinceStartup;
    41.        
    42.         List<UnityThreading.Task> taskList = new List<UnityThreading.Task>();
    43.         for(i=0;i<CoresCount;i++){
    44.             var j = i;
    45.             taskList.Add(UnityThreadHelper.TaskDistributor.Dispatch(() => ThreadFunction("testB",j))); 
    46.         }
    47.         for(i=0;i<CoresCount;i++){
    48.             taskList[i].Wait();
    49.         }
    50.         taskList.Clear();
    51.        
    52.         ThreadTime2 = Time.realtimeSinceStartup - ThreadTime2;
    53.        
    54.     }
    55.  
    56.     public void ThreadFunction(string stringVar, int integerVar){
    57.         // do something big here
    58.         Debug.Log(stringVar+" "+integerVar);
    59.         for(int i=0;i<500000;i++){
    60.             float n = Mathf.Cos(Mathf.Sqrt(Mathf.Atan(2f)));
    61.         }
    62.     }
    63.    
    64.     private class threadsManager{
    65.         private static List<customThread> taskList = new List<customThread>();
    66.        
    67.         public static bool StartThread(Action function){
    68.  
    69.             int i=0;
    70.             for(i=0; i<taskList.Count;i++){
    71.                 if(!taskList[i].running){
    72.                     taskList[i].Start(function);
    73.                     return true;
    74.                 }
    75.             }
    76.  
    77.             taskList.Add(new customThread());
    78.  
    79.             taskList[i].Start(function);
    80.            
    81.             return true;
    82.         }
    83.        
    84.         public static void Wait(){
    85.             while(customThread.count>0);
    86.         }
    87.     }
    88.    
    89.     private class customThread{
    90.         public static int count = 0;
    91.        
    92.         public bool running = false;
    93.  
    94.         private Action function;
    95.         private Thread myThread;
    96.        
    97.         public customThread(){
    98.         }
    99.        
    100.         private void doFunction(System.Object stateInfo){
    101.             function();
    102.             running = false;
    103.             if(count>0) count--; // function is finished, decrease the running threads counter
    104.         }
    105.  
    106.         public void Start(Action f){
    107.             function = f;
    108.             count++; // new thread is started, increase the running threads counter
    109.             running = true;
    110.             ThreadPool.QueueUserWorkItem(doFunction);
    111.         }
    112.  
    113.     }
    114. }
    115.  
    116.  
     
  36. unisip

    unisip

    Joined:
    Sep 15, 2010
    Posts:
    321
    Hi there,

    Great stuff you've got here, thanks for sharing!!!
    I started playing around with it a bit and could easily get some tasks dispatched. However, trying to test the HasEnded property, I could never see it turn to TRUE.

    Here is what I'm doing:

    Task t;

    void Foo(int a, int b)
    {
    //do something BIG

    //verify that Foo reaches its end
    UnityThreadHelper.Dispatcher.Dispatch(() => Debug.Log("done..."));
    }

    Start()
    {
    t = UnityThreadHelper.TaskDistributor.Dispatch(() => Foo(0, 100)));
    }

    OnGUI()
    {
    GUILayout.Label(t.HasEnded.ToString());
    }


    I do see the "done..." in the console that ensures that foo ends, but t.HasEnded never turns to TRUE.

    Am I missing something?

    Tks
     
  37. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Thanks for reporting this issue, this is indeed a bug. You can fix it by adding:
    Code (csharp):
    1.  
    2. endedEvent.Set();
    3.  
    before the first return; in Task.cs -> TaskBase.DoInternal:

    Code (csharp):
    1.  
    2.  var enumerator = Do();
    3.                 if (enumerator == null)
    4.                 {
    5. endedEvent.Set(); // add this line!
    6.                     return;
    7.                 }
    8.  
    I will reupload the fixed version, soon.

    Fixed it:
    View attachment 35029
     
    Last edited: May 25, 2012
  38. murteas

    murteas

    Joined:
    Feb 14, 2012
    Posts:
    62
    Just wanted to reply to say - Thank you! I just found this and it is incredibly helpful.
     
  39. tylo

    tylo

    Joined:
    Dec 7, 2009
    Posts:
    153
    I can't tell you what broke this, as I have no idea. It was working earlier today and simply stopped working later.

    Everytime I call

    Code (csharp):
    1. var task = UnityThreadHelper.TaskDistributor.Dispatch(() => Derp());
    I get the error:

    NullReferenceException: Object reference not set to an instance of an object

    Where Derp() is defined as:

    Code (csharp):
    1.     void Derp(){
    2.         Debug.Log("derp"); 
    3.     }
    4.  
     
  40. tylo

    tylo

    Joined:
    Dec 7, 2009
    Posts:
    153
    Again, I'm not sure what happened, but removing the UnityThreading files from my project and readding them did the trick.
     
  41. tylo

    tylo

    Joined:
    Dec 7, 2009
    Posts:
    153
    Actually I spoke too soon. This same problem just started happening again, and removing/readding the UnityThreading files does not seem to fix it.
     
  42. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Is this a general problem with the helper? Could you post the whole error (including the stack trace)?
     
  43. tylo

    tylo

    Joined:
    Dec 7, 2009
    Posts:
    153
    I can post the stack trace, but it unfortunately does not give much information. It doesn't display there being any problem with anything inside of your code. It simply traces the stack of where I eventually call the ThreadHelper method on line 191 of ShelfEditor.

    Code (csharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. ShelfEditor.ReloadPSA (.Shelf shelf) (at Assets/ShelfCore/Scripts/ShelfEditor.cs:191)
    3. ShelfEditor.ReloadAllShelves () (at Assets/ShelfCore/Scripts/ShelfEditor.cs:209)
    4. ShelfEditor.Start () (at Assets/ShelfCore/Scripts/ShelfEditor.cs:50)
    I deleted and re-added the UnityThreading folder once again and it seems to work.
     
  44. nitz

    nitz

    Joined:
    May 4, 2011
    Posts:
    51
    For a long time, Co-routines have been as close to running multiple threads as I've ever needed in Unity. Today, I came across something that I absolutely needed to do on it's own thread. I shuddered at the amount of work I'd need to do to work around the non-thread-safeness of unity, then I found your UnityThreadHelper, Marrrk.

    You've saved me countless hours of debugging and headaches. I can't believe you're giving away something this nice. I'll never be able to thank you enough.
     
  45. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    I did some work on Unity API Thread safety (the lack of a better word for it. i use thread safety to describe it).

    I created several classes (around 200, not every one is needed I guess..) who wrap the Unity API classes, these wrapper classes will allow you to use the Unity API without much work with the Dispatcher (internally it will work with it).

    For example:

    Without the thread safety wrapper classes:
    Code (csharp):
    1.  
    2. void DoThreadWork()
    3. {
    4.     var health = Mathf.Random(10, 100);
    5.     UnityThreadHelper.Dispatcher.Dispatch(() =>
    6.     {
    7.         var newGameObject = GameObject.Instantiate(Resources.Load(objectToInstantiate));
    8.         newGameObject.GetComponent<MyComponent>().health = health;
    9.     }
    10. }
    11.  
    With the safety wrapper:
    Code (csharp):
    1.  
    2. using UnityThreadHelper;
    3. ...
    4. void DoThreadWork()
    5. {
    6.     var health = Mathf.Random(10, 100);
    7.    
    8.     var loadedObject = ThreadSafe.Resources.Load(objectToInstantiate)
    9.     var newGameObject = (ThreadSave.GameObject)ThreadSafe.GameObject.Instantiate(loadedObject);
    10.    
    11.     newGameObject.GetComponent<MyComponent>().health = health;
    12. }
    13.  
    Some of these things could be improved (like casting to the ThreadSafeXYZ types and the naming of these.)

    Tell me what you think about this.
     
  46. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    The AssetStore version now contains a UnityThreading.Threadsafe.dll assembly, this assembly wrapps almost all UnityEngine namespace classes. Those wrapper classes can be cast from and to UnityEngine classes and allow the usage inside of an thread as if those thread is the main thread:

    tl;dr: UnityAPI is usable inside the threads.

    Usage:

    Inside a thread you need to cast your UnityObject to the ThreadSafe form:

    Code (csharp):
    1.  
    2. var threadSafeGameObject = (UnityThreading.ThreadSafe.GameObject)myGameObject;
    3.  
    All static methods are accesseable like before, you only need to use the ThreadSave form:
    Code (csharp):
    1.  
    2. UnityThreading.ThreadSafe.Object.Destroy(someObject)
    3.  
    Also:

    API Reference of the vanilla UnityThreadinHelper stuff:
    http://dras.biz/download/UnityThreadHelper/Help/Index.html
     
    Last edited: Jul 18, 2012
  47. ZJP

    ZJP

    Joined:
    Jan 22, 2010
    Posts:
    2,649
    at last...
    Well done. ;)
     
  48. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    I have only looked at the free version so far but will likely buy the Asset Store version. This is good work- thanks! It is nicely designed and seems to work well so far. Dispatch<T>( () => {}) is brilliant stuff!

    Just one question so far... would it be possible to extend TaskDistributor to have a set of context objects that can be re-used for each thread? For example, a pool of database connections? Let me know if you have any suggestions or pointers along those lines.

    edit;
    OMG! your TaskDistributor is smoking fast. I just did 2000 Redis queries in ~5 seconds , and that's even *with* going back to the main thread to get a Redis connector from a pool on there. And on a low-end laptop cpu. Couldn't be happier. This is worth way more than $10 bucks!
     
    Last edited: Jul 26, 2012
  49. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Thanks :)

    Do you mean somethink like this:

    Code (csharp):
    1.  
    2. TaskProc()
    3. {
    4.   var dbConnection = dbConnectionPool.Take();
    5.  
    6.   ... // Do stuff with dbConnection
    7.  
    8.   dbConnectionPool.Release(dbConnection);
    9. }
    10.  
    This would be something independent to the TaskDistributor I think. But please tell me more, maybe I will extend the TaskDistributor to allow such things.
     
  50. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    Marrk, yes exactly. The thing is the connection pool needs to be thread safe too. I'm not sure if it belongs in TaskDistributor either, really. This is what I ended up writing yesterday, and it seems to work fine for me so I guess I'll just go with it, unless you see any flaws.
    Code (csharp):
    1. public void ReleaseRedis( RedisDataAccessProvider redis )
    2.     { // put the redis back into pool
    3. ... }  
    4.     public RedisDataAccessProvider GetRedis()
    5.     { // get a redis, or Thread.Sleep until one is available
    6. ... }
    7. // But must call that on the main thread, from any workers!
    8. var task = UnityThreadHelper.Dispatcher.Dispatch<RedisDataAccessProvider>( () => {
    9.             return RedisComponent.instance.GetRedis();
    10.         });    
    11.         task.Wait();       
    12.         var redis = task.Result;           
    13.         // do work...
    14. UnityThreadHelper.Dispatcher.Dispatch( () => {
    15.             RedisComponent.instance.ReleaseRedis(redis);
    16.         });