Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Unity Threading Helper

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

  1. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    I would suggest that you use locks for this, no need for a dispatcher here :)

    Code (csharp):
    1.  
    2. public class RedisDataAccessProviderList : IDisposable
    3. {
    4.     private Queue<RedisDataAccessProvider> availableProviders = new Queue<RedisDataAccessProvider>();
    5.     private List<RedisDataAccessProvider> allProviders = new List<RedisDataAccessProvider>();
    6.    
    7.     public RedisDataAccessProvider GetRedis()
    8.     {
    9.         lock (availableProviders)
    10.         {
    11.             if (availableProviders.Count == 0)
    12.             {
    13.                 return CreateRedis();
    14.             }
    15.             return availableProviders.Dequeue();
    16.         }
    17.     }
    18.    
    19.     public void ReleaseRedis( RedisDataAccessProvider redis )
    20.     {
    21.         lock (availableProviders)
    22.         {
    23.             lock(allProviders)
    24.             {
    25.                 if (allProviders.Contains(redis))
    26.                 {
    27.                     availableProviders.Enqueue(redis);
    28.                 }
    29.             }
    30.         }
    31.     }
    32.    
    33.     public void Dispose()
    34.     {
    35.         lock (availableProviders)
    36.         {
    37.             lock(allProviders)
    38.             {
    39.                 foreach (var provider in allProviders)
    40.                     provider.Dispose();
    41.                    
    42.                 allProviders.Clear();
    43.                 availableProviders.Clear();
    44.             }
    45.         }
    46.     }
    47.    
    48.     private RedisDataAccessProvider CreateRedis()
    49.     {
    50.         lock (allProviders)
    51.         {
    52.             var newProvider = new  RedisDataAccessProvider(); // change as needed
    53.             allProviders.Add(newProvider);
    54.             return newProvider;
    55.         }
    56.     }
    57. }
    58.  
    Code (csharp):
    1.  
    2. // But must call that on the main thread, from any workers!
    3. var redis = RedisComponent.instance.GetRedis();  
    4. // do work...
    5. RedisComponent.instance.ReleaseRedis(redis);
    6.  
    securing RedisComponent.instance is an other thing you need to do.
     
  2. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    Good call, re: using locks! I converted your code to use HashSet<T> and also there was a missing availableProviders.Add() in GetRedis().
    Yet another performance boost :)
     
  3. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Mh, for what do you need an availableProviders.Add() in GetRedis()? CreateRedis() already adds the reference needed ;)
     
  4. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    Oh yeah- that's redundant never mind :)
     
  5. VeTaL

    VeTaL

    Joined:
    Jul 2, 2012
    Posts:
    125
    Just a fast question: is it possible (effective) to run procedural entities creation in another thread?

    As i understood, usually, i should put all calculation stuff to another thread
    Code (csharp):
    1.  
    2.         verts.Add(new Vector3(x, y, z));
    3.         verts.Add(new Vector3(x + 1, y + 1, z));
    4.         verts.Add(new Vector3(x + 1, y, z));
    5.         verts.Add(new Vector3(x, y + 1, z));
    6.  
    7.         colors.Add(new Color(shade, shade, shade));
    8.         colors.Add(new Color(shade, shade, shade));
    9.         colors.Add(new Color(shade, shade, shade));
    10.         colors.Add(new Color(shade, shade, shade));
    11.  
    but call finalizing stuff from the main thread
    Code (csharp):
    1. subMesh.vertices = verts.ToArray();
    2.         subMesh.triangles = indices.ToArray();
    3.         subMesh.uv = uvs.ToArray();
    4.         subMesh.colors = colors.ToArray();
    5.        
    6.         subMesh.Optimize();
    7.         subMesh.RecalculateNormals();
    Whats about your library?
     
  6. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Yep, this is possible:

    Code (csharp):
    1.  
    2. verts.Add(new Vector3(x, y, z));
    3. verts.Add(new Vector3(x + 1, y + 1, z));
    4. verts.Add(new Vector3(x + 1, y, z));
    5. verts.Add(new Vector3(x, y + 1, z));
    6.  
    7. colors.Add(new Color(shade, shade, shade));
    8. colors.Add(new Color(shade, shade, shade));
    9. colors.Add(new Color(shade, shade, shade));
    10. colors.Add(new Color(shade, shade, shade));
    11.  
    12. Dispatch(() =>
    13. {
    14.     subMesh.vertices = verts.ToArray();
    15.     subMesh.triangles = indices.ToArray();
    16.     subMesh.uv = uvs.ToArray();
    17.     subMesh.colors = colors.ToArray();
    18.  
    19.     subMesh.Optimize();
    20.     subMesh.RecalculateNormals();
    21. });
    22.  
     
  7. SevenBits

    SevenBits

    Joined:
    Dec 26, 2011
    Posts:
    1,953
    This looks incredibly cool!
     
  8. sonicviz

    sonicviz

    Joined:
    May 19, 2009
    Posts:
    1,051
    Hi,
    I have an application using system.threading.timer and associated callback to do some musical event timing work.
    Would your package allow me to also do this in a thread safe manner?

    ty!
     
  9. UnlimitedEdition

    UnlimitedEdition

    Joined:
    Feb 27, 2012
    Posts:
    287
    I don't know if this has already been asked but, if I try and use a thread that isn't there so to speak, will the computer explode. I say this because not every computer will have 4 threads or 8 threads or 2 threads (Well any computer should have 2 threads but you get the point). So if I tell the code to "allocate" four threads when there's only two, what happens.
     
  10. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    @Pikrat, generally speaking, more threads can always be created (within limits), even if there is only 1 core. The OS multitasks the different threads across available cores. But generally you want to consider the number of processor cores available. For example the taskdistributor class by default creates a thread pool sized at # of cores * 3. How this works in practice with Unity on mobile devices, older single core devices, I hope to find out soon - I'm testing this today actually.
     
    Last edited: Aug 6, 2012
  11. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    @sonicviz: This depends, you can always call the dispatcher at gametime, this will perform the workload on the mainthread and everything should be ok. True threadsafety can be archived by using the different lock methods (the most easy way to do this will be the lock keyword self),

    When you want to all UnityAPI calls on an other thread than the mainThread, this is impossible, but with the UnityThreadHelper you can make your life easier (using the dispatcher or using one of the "threadsafe" classes).

    Could you give me an example of what you want to archive? Maybe I can give a better answer with this as base.

    @Pikrat: mindlube already said everything about this, using more threads than available cores should be no problem, you should only ensure to not create a massive amount of threads, first there is an OS limit, second creating threads is a costly operation (performance) and third, switching between each thread (the OS does this for you) also costs some time so that creating too much threads will result in more time used for switching threads than using the threads self. But this are problems you will not encounter when not creating a massive amount of threads.

    When you need to perform many background operations, the best way to do this is using the TaskDistributor as mindlube suggested.

    @mindlube: Yes currently its 3*Cores, but I have seen that using only 2 Cores will increase the speed of operating each task. This can be changed of course :)

    Greetings, Mark
     
  12. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    Mark, is there any way to easily kill off all the threads which have been started with UnityThreadHelper.TaskDistributor.Dispatch() ?
    Or do I have to save references to all the TaskBase and call Abort on them? thanks
     
  13. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    If you want to kill the tasks manually you need to store the reference to them and call Abort.

    The TaskDistributor contains X Threads which will be managed by the TaskDistributor self, the TaskDistributor will release all threads automatically. When no tasks run on the TaskDistributor, the associated Threads will all sleep until a new Task arrives.
     
  14. Gobla

    Gobla

    Joined:
    May 28, 2012
    Posts:
    354
    I'm having a strange bug.
    After handling loads of data trough the threading helper, the data becomes unaffected by garbage collection or any other form of memory cleaning after the task has been completed and disposed afterwards.

    This results in out of memory crashes due to > 4 Gb memory usage. Unity reports that after the tasks, it is only using 500 mb of ram.

    Any idea on how this is happening and how to possible fix this?

    And also: Great plugin!
     
  15. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Hi there, could you post a sample which shows this behaviour?
     
  16. Gobla

    Gobla

    Joined:
    May 28, 2012
    Posts:
    354
    Some snippets from my code to show the concept.

    Looping this code is fast but results in increasing amounts of memory used. Task manager shows many times more memory used compared to the profiler. The memory never clears, not even after forcing a Garbage collection every minute or so.
    Code (csharp):
    1.        
    2. for (int i = 1; i < = 50; i++)
    3. {
    4.     var task = UnityThreadHelper.TaskDistributor.Dispatch(() => TerrainLoader.LoadHeightMap(i) );
    5.     float[,] map = task.Result;
    6.  
    7.     // Do something with the map variable
    8.  
    9.     task.Dispose();
    10. }
    11. System.GC.Collect();
    Looping this code is quite a bit slower then the above and results in less memory used. However it still always uses more memory compared to the method below but is also faster. Also with very large data sets I could see that the memory wouldn't clear.
    Code (csharp):
    1.    
    2. var task = UnityThreadHelper.TaskDistributor.Dispatch(() => {  
    3.     for (int i = 1; i < = 50; i++)
    4.     {
    5.         float[,] map =  TerrainLoader.LoadHeightMap(i) ;
    6.    
    7.         // Do something with the map variable
    8.     }
    9. });
    10. task.Dispose();
    11. System.GC.Collect();
    Looping this code 50 times is slow but doesn't results in increasing amounts of memory used. The profiler shows a constant amount of memory which is a bit smaller then shown in the task manager (due to the overhead of unity running).
    Code (csharp):
    1.        
    2. for (int i = 1; i < = 50; i++)
    3. {
    4.     float[,] map = TerrainLoader.LoadHeightMap(i);
    5.  
    6.     // Do something with the map variable
    7. }
    8.  
    9. System.GC.Collect();
    There is no difference in a windows exe compiled version. Pre-declaring the variable "map" outside the loop and setting it to null after the loop has no effect either. This has been tested on both a classic quad core (striped duo-core) and a core i7.
     
  17. Marrrk

    Marrrk

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

    I tried to replicate the problem:

    Code (csharp):
    1.  
    2. var dispatcher = new Dispatcher();
    3. var taskDistributor = new TaskDistributor();
    4.  
    5. var thread = new ActionThread((at) =>
    6.     {
    7.         for (int i = 0; i < 5000; ++i)
    8.         {
    9.             var task = taskDistributor.Dispatch(() => { return new float[2000, 2000]; });
    10.             float[,] result = task.Result;
    11.         }
    12.         Console.WriteLine("done");
    13.     });
    14.  
    15. while (true)
    16. {
    17.     dispatcher.ProcessTasks();
    18. }
    19.  
    But I was not able to observe the problem you described. Do I miss something? Does your TerrainManager do something interesting regarding the problem?

    Btw: You do not need to call Dispose manually ;)

    Greetings
     
  18. Gobla

    Gobla

    Joined:
    May 28, 2012
    Posts:
    354
    Thanks about the tip of Dispose.

    I think i've found the problem (however I can't test at at the moment).
    The function is running in a coroutine. In some of the coroutines (not all). I've used some (what I presume) obsolete code. I think this will result in some kind of racing and problems with properly disposing taks when there are like 500 tasks running...
    Code (csharp):
    1.  
    2. while (!task.HasEnded) { yield return null; }
    3.  
    Going to test this tonight.
     
  19. Gobla

    Gobla

    Joined:
    May 28, 2012
    Posts:
    354
    Sorry, I haven't been able to test it yet due to a big exam for tomorrow.
     
  20. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    Marrrk, what am I doing wrong here? I want to use TaskDistributor to queue up a bunch of actions which are a simulation. The simulation has a combinatorial # of possibilities so it's known that we don't have enough time to finish the simulation, so need to kill off the threads and use whatever work they have done. Here is my basic usage. What I am seeing is the threads never stop (until I go out of play mode in the editor, then the threads do stop with the "thread stopping" log message)

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityThreading;
    5.  
    6. public class NewBehaviourScript : MonoBehaviour
    7. {
    8.     public IEnumerator Start ()
    9.     {
    10.         var threads = new HashSet<TaskBase>();
    11.        
    12.         // queue up 100 thread tasks
    13.         for (int i = 0; i < 100; i++)
    14.         {          
    15.             var task = UnityThreadHelper.TaskDistributor.Dispatch( () => {
    16.                 Debug.Log ("thread starting");
    17.                 while(true)
    18.                 {
    19.                     if(ActionThread.CurrentThread.ShouldStop)
    20.                     {
    21.                         Debug.Log ("thread stopping.");
    22.                         return;
    23.                     }
    24.                     Debug.Log ("thread running.");
    25.                 }
    26.             });
    27.             threads.Add(task);
    28.         }
    29.        
    30.         Debug.Log ("queued up some threads");
    31.        
    32.         yield return new WaitForSeconds(2f);
    33.        
    34.         foreach(var thread in threads)
    35.         {
    36.             thread.Abort();
    37.         }
    38.        
    39.         foreach(var thread in threads)
    40.         {
    41.             while( ! (thread.HasEnded || thread.IsFailed || thread.IsSucceeded ))
    42.             {
    43.                 Debug.Log ("waiting for threads to abort...");
    44.                 yield return null;
    45.             }
    46.         }
    47.     }
    48. }
     
  21. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    First of all, a task is not a thread! A task is only a action which will be processed on a certain thread, thats all ;)

    What you are doing is basically this: You create 100 Tasks who will run as long as possible only stopping when the Thread which will process the tasks should stop. The TaskDistributor threads only stop when you exit the play mode.

    What you should do is: abort the while loop of your task when the task should stop:

    Instead:
    Code (csharp):
    1.  
    2. while (true)
    3.  
    Do this:
    Code (csharp):
    1.  
    2. TaskBase task;
    3. task = UnityThreadHelper.TaskDistributor.Dispatch( () => {
    4.   Debug.Log ("thread starting");
    5.   while (!task.ShouldAbort)
    6.  
    But this will still not help you very well. Per default the TaskDistributor creates 3* Number of Cores threads, those threads will process your tasks, when you run your tasks as long as they are not aborted, only 3 Tasks will be processed at maximum when you only have one core.
     
  22. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    Code (csharp):
    1.  
    2. TaskBase task;
    3. task = UnityThreadHelper.TaskDistributor.Dispatch( () => {
    4.   Debug.Log ("thread starting");
    5.   while (!task.ShouldAbort)
    6.  
    Marrrk, thanks for the help. In your above code the lambda refers to an outer scoped variable, task, which is fine, but in any nontrivial usage, many tasks will be created, and the task reference could change, and the task may no longer point to the task that is actually checking the task.ShouldAbort. If the goal is just to kill off all tasks, however that should work fine. But I get your point about the difference between tasks and threads. This must be why TaskDistributor.Dispatch has 3 overloaded methods vs. CreateThread which has like 8 overloaded methods :) Anyways, I think I am good for now.
     
  23. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    You can inherit from TaskBase if you want direct access to the currently running task instance ;)
     
  24. Gobla

    Gobla

    Joined:
    May 28, 2012
    Posts:
    354

    A strange bug involving (pseudo) singletons and the threading helper resulted in this problem. The TerrainManager wasn't a true singleton. Whenever i would call it trough a the threading helper, a copy of it would be written to the memory. However it would never be removed from the memory after it was no longer used...

    I've rewritten the singleton to a safe locked mode and it is working fine now. But thanks for looking into it :D.
     
  25. smb02dunnal

    smb02dunnal

    Joined:
    May 5, 2012
    Posts:
    439
    If I wanted to use an array of variables in a thread created with this tool and use the info in the array in the unity thread, how would I go about doing this?

    The current setup I have uses locks, however, the working thread i created spontaneously stops running without any warning or exception. Any ideas on this?
     
  26. rockysam888

    rockysam888

    Joined:
    Jul 28, 2009
    Posts:
    650
    (bookmarked)
     
  27. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Could you give me a sample (code)?
     
  28. BTamas

    BTamas

    Joined:
    Jul 18, 2012
    Posts:
    15
    Hello

    We have been using threadhelper in our porject for months, and as time went on, we encountered several anomalies.

    1.: Invisible gameobject
    On some of our scenes, there was an invisible threadhelper gameobject in editor and play mode. In the attached project, check the threadhelperTest scene.
    Load the scene
    In edit mode:

    • In the menu press "Threadhelper/Threadhelper present" editor script
    • Delete the one gameobject in the scene ("Title")
    • The invisible [ThreadHelper] object will magically appear in the Hierarchy

    Alternatively also in edit mode:
    • dont run the Threadhelper present editor script first
    • delete the Title gameobject.
    • invisible threadhelper object will not appear in the Hierarchy

    The only way that is know to us, to delete the invisible gameobject in the editor, is the "Threadhelper/Kill threadhelper" editor script, which is in the sample project.

    2.:
    The other "problem" is, the taskDistributor wont work (nullpointer), if we dont call createthread first, if we put threadhelper on the scene manually (and it wont be destroyed on scene change).
    (Guess what we had already on the scene, hiding under a cloak of invisibility.. :))

    We bought the paid version today, (10$ is crazy cheap....) and now we can't edit the ThreadHelper class, because it is a dll, so we won't be able to put the threadhelper script on the scene anymore.

    If we dont put the threadhelper class on a gameobject manually, then on scene change, we would have to re-init the whole threadhelper object.
    Is that supposed to be a good thing? We're trying to be efficient, and we have a lot of scene changes in our project. So we tought that re-doing the threadhelper object on every scene change would be bad.

    On the other hand, if we dont kill the threadhelper GO on scene change, after a lot(or not a lot..it seems to be random) of scene changes and usage of the task distributer, it tends to "hang".
    Example: if we have 2 scenes that load list items from database on Start(), and we kepp changing between those 2 scenes, sometimes the taskditributor will never return to the ui thread, and the ui will be waiting indefinitely.
    In the logs, it looks like the background task just stopped at a random line.

    So, to sum it up, if we want to put the threadhelper script on the scene, we have to change the ThreadHelper like this:

    Code (csharp):
    1. public class UnityThreadHelper
    2. ...
    3. public static void EnsureHelper()
    4.     {
    5.         if (null == (object)instance)
    6.         {
    7.             instance = FindObjectOfType(typeof(UnityThreadHelper)) as UnityThreadHelper;
    8.             if (null == (object)instance)
    9.             {
    10.                 var go = new GameObject("[UnityThreadHelper]");
    11.                 go.hideFlags = HideFlags.NotEditable | HideFlags.HideInHierarchy | HideFlags.HideInInspector;
    12.                 instance = go.AddComponent<UnityThreadHelper>();
    13.                 instance.EnsureHelperInstance();
    14.             } else { //if we dont do this here, and put the script on the scene, taskdistributor will only be usable, if you call createthread first, which will call the EnsureHelperInstance()
    15.         instance.EnsureHelperInstance();
    16.         }
    17.         }
    18.     }
    19. ...
    In the taskdistributorTest Scene there is already a threadhelper object if you play the scene, and press the Start taskdistributor button, it will throw a nullpointer.
    If you press the createthread, than the start taskdistributor, then it will be ok.

    Also, the class TaskDistributor is in the file TaskDistributer.cs :)
     

    Attached Files:

  29. BTamas

    BTamas

    Joined:
    Jul 18, 2012
    Posts:
    15
    Here is how to reproduce the taskdistributer nullpointer, with scene changes:

    1#
    Code (csharp):
    1. UnityThreadHelper.TaskDistributor.Dispatch (() => {
    2.     Debug.Log ("Taskdistributor started OK");
    3. });
    everything ok.

    2#
    reload level
    Code (csharp):
    1. Application.LoadLevel("taskdistributorTest");
    still ok.
    3#
    Code (csharp):
    1. UnityThreadHelper.TaskDistributor.Dispatch (() => {
    2.     Debug.Log ("Taskdistributor started OK");
    3. });
    ^ nullpointer

    if createThread would be called before 3#, then it would work fine.

    Our workaround now would be to not destroy the threadhelper GO with DontDestroyOnLoad().
     
  30. Marrrk

    Marrrk

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

    you are right, the behaviour is a bug, the ThreadHelper GO should always work in playmode. The core problem here is that when the GO will be destroyed the static instance will not be set to null. I will fix this in ~8hours.

    The other problem is that the UnityThreadHelper Object is only tested for the playmode, It should NOT work in the raw editor mode (which makes the usage of editor scripts who base upon the UnityThreadHelper Object impossible due the fact that the Update() method will never be called, the Update() method is needed for the dispatcher).

    It is possible to create a safer version of the object and an extra version for the editor, something like:

    UnityThreadHelper: Gives an error when used inside the Editor Mode
    UnityEditorThreadHelper: Only usable inside the Editor Mode

    You can also redo the whole thing the UnityThreadingHelper object does, the startpost contains the zipped version of the whole SourceCode of the base classes. UTH does not rely on the UTH GameObject ;)

    Greetings Mark
     
  31. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    I have updated the asset store version and added the full VS Source Files.
     
  32. BTamas

    BTamas

    Joined:
    Jul 18, 2012
    Posts:
    15
    I have downloaded the update, but the UnityThreadHelper.dll does not seem to be different.
    A changelog would be nice :p
     
  33. ZJP

    ZJP

    Joined:
    Jan 22, 2010
    Posts:
    2,649
    +1. A Sort of "GetVersion" in UnityThreadHelper.dll/UnityThreadHelper.ThreadSafe.dll will be cool. ;)
     
  34. BTamas

    BTamas

    Joined:
    Jul 18, 2012
    Posts:
    15
    Hello

    Another issue:
    We are experiencing issues, in whitch our app just stops at a point, where we use UnityThreadHelper.Dispatcher.Dispatch to call the ui thread.
    Here is an example. We are not sure, if it is supposed to work like this, please Marrrk have a look.

    Code (csharp):
    1.        
    2. ...from ui thread:
    3. UnityThreadHelper.TaskDistributor.Dispatch(() => {
    4.     DoSomethingInBackgroundNow();
    5.            
    6.     for (int i = 0; i < 10; i++) {
    7.         UnityThreadHelper.Dispatcher.Dispatch (() => {
    8.             UpdateUi (i);
    9.         }).Wait ();
    10.     }
    11. });
    12. ...
    13.    
    14. private void UpdateUi(){
    15.     DoSomeOtherUiOnlyStuff();
    16.        
    17.     UnityThreadHelper.TaskDistributor.Dispatch(() => {
    18.         DoSomeMoreBackgroundStuff();
    19.        
    20.         for (int i = 0; i < 10; i++) {
    21.             UnityThreadHelper.Dispatcher.Dispatch (() => {
    22.                 UpdateUiWithSomethingElse (i);
    23.                 //this is the part, where our code hangs ~1 out of 100 runs
    24.             }).Wait (); //could this wait block the ui thread infinitely?
    25.         }
    26.     });
    27. }
     
  35. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    @BTamas: The Assembly is exactly equal? I will have a look into this.

    @ZJP: You are right I will add a meaningfull assembly version (currently its always 1.0.0.0)

    @BTamas:

    All things dispatched by the Dispatcher.Dispatch method will be run the next time the Update() methods will be called, so dispatching something from the UI thread to the UI thread will always block. But your problem seems to be something else. Do you lock something?

    The Wait() you marked should not block the UI infinitely.
     
  36. BTamas

    BTamas

    Joined:
    Jul 18, 2012
    Posts:
    15
    @Marrrk

    We do use locks, but not around the parts that tend to just hang.
    Here is another example, could you imagine a situation, where this could die?

    This is running in taskdistributor:

    Code (csharp):
    1. UnityThreadHelper.Dispatcher.Dispatch(() => {
    2.     Caching.IsVersionCached (item.fileName, item.version); //runs fine
    3.     Debug.log ("This log will run too");
    4. }).Wait();
    5. Debug.log ("This will never be called");
     
  37. AlexZ

    AlexZ

    Joined:
    Sep 17, 2009
    Posts:
    263
    I am not sure I understand, can we use this with the Unity3d WWW object to download asset bundles?

    I tried to do this and my editor is crashing when I test, I guess WWW is not thread safe. Any way to make this work?

    Thanks! :D
     
  38. BTamas

    BTamas

    Joined:
    Jul 18, 2012
    Posts:
    15
    Also, we noticed this:

    Caching.IsVersionCached (item.fileName, item.version);
    As far as we know, this should be used on the ui thread only, because it is a Unity method, and it will fail if we call it from a thread.

    Code (csharp):
    1. UnityThreadHelper.CreateThread ((obj) => {
    2.     Caching.IsVersionCached (item.fileName, item.version); //this will work on android and iOS builds, but in the editor on pc it will throw an error: IsVersionCached  can only be called from the main thread.
    3. });
    4.  
    5.  
    6. UnityThreadHelper.CreateThread ((obj) => {
    7.     UnityThreadHelper.Dispatcher.Dispatch (() =>
    8.     {
    9.         Caching.IsVersionCached (item.fileName, item.version); //this will run on every build target (pc,ios,android) without an exception, except that it would hang sometimes and die
    10.     }).Wait();
    11. });
    12.  
     
    Last edited: Oct 3, 2012
  39. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    I found the bug, sometimes new tasks were not acknowledged when the timing of adding a new task and finishing an old one was right. I fixed it in the free/basic version and also in the asset store version, the asset store version is still pending. I will report back when the asset store version has been accepted.

    Also the Assemblies in the AssetStore version are now properly versioned.

    @BTamas: I saw that you use the asset store version, with this it is now very easy to call Unity API methods without the need to write manual Dispatcher calls, have a look into the latest version, which includes an example for this :)

    Edit: It has been accepted.
     
    Last edited: Oct 4, 2012
  40. AlexZ

    AlexZ

    Joined:
    Sep 17, 2009
    Posts:
    263
    @Marrk, Could I get some help with my above WWW question please?
     
  41. SevenBits

    SevenBits

    Joined:
    Dec 26, 2011
    Posts:
    1,953
    This is pretty cool.
     
  42. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    I am sorry, I must have missed to answer you.

    All Unity API calls are not thread safe, at least they will report an error when using them outside the main thread. But, the Helper is able to execute everything on the main thread to overcome this problem by using the Dispatcher, BTamas, for example, did this to call the Caching methods.

    When you are using the asset store version, a special namespace exists which has most/all Unity classes replicated with the ability to be called from an other thread which overcomes this problem (these classes are simple wrapper classes who are using the Dispatcher).

    Could you elaborate what you want to do? The WWW class should already be working with threads.
     
  43. BTamas

    BTamas

    Joined:
    Jul 18, 2012
    Posts:
    15
    @BTamas: I saw that you use the asset store version, with this it is now very easy to call Unity API methods without the need to write manual Dispatcher calls, have a look into the latest version, which includes an example for this :)

    We already tried that. When we put the threadsafe dll into our project, we can't build our project for iOS.

    Code (csharp):
    1.  Cross compilation job UnityThreadHelper.ThreadSafe.dll failed.
    2. UnityEngine.UnityException: Failed AOT cross compiler: /Applications/Unity/Unity.app/Contents/BuildTargetTools/iPhonePlayer/mono-xcompiler --aot=full,asmonly,nodebug,nimt-trampolines=512,static,outfile="UnityThreadHelper.ThreadSafe.dll.s" "UnityThreadHelper.ThreadSafe.dll"  current dir : /Developer-old/Teszt/Temp/StagingArea/Data/Managed
    3. result file exists: False
    4. stdout:
    5. stderr:
    6.  
    7.  at UnityEditor.MonoProcessUtility.RunMonoProcess (System.Diagnostics.Process process, System.String name, System.String resultingFile) [0x00000] in <filename unknown>:0
    8.  at UnityEditor.MonoCrossCompile.CrossCompileAOT (BuildTarget target, System.String crossCompilerAbsolutePath, System.String assembliesAbsoluteDirectory, CrossCompileOptions crossCompileOptions, System.String input, System.String output, System.String additionalOptions) [0x00000] in <filename unknown>:0
    9.  at UnityEditor.MonoCrossCompile+JobCompileAOT.ThreadPoolCallback (System.Object threadContext) [0x00000] in <filename unknown>:0
    10. UnityEditor.MonoCrossCompile:CrossCompileAOTDirectoryParallel(BuildTarget, CrossCompileOptions, String, String, String)
    11. PostProcessiPhonePlayer:PostProcess(BuildTarget, String, String, String, String, String, String, String, BuildOptions, RuntimeClassRegistry)
    12. UnityEditor.BuildPlayerWindow:BuildPlayerAndRun()
     
  44. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Well, thats odd. Any idea what might cause this? I dont own an IOS device and the error message does not state whats wrong.
     
  45. Zergling103

    Zergling103

    Joined:
    Aug 16, 2011
    Posts:
    392
    UnityThreadHelper.TaskDistributor is null for me. Why is this?
     
  46. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    It will be set null when changing scenes, but should be recreated when accessing something from the UnityThreadHelper. Do you have a sample for this behaviour I can look into?
     
  47. Zergling103

    Zergling103

    Joined:
    Aug 16, 2011
    Posts:
    392
    Nothing that I can freely give away right now, but I'll try to get a sample ready sometime this week, hopefully.
     
  48. AlexZ

    AlexZ

    Joined:
    Sep 17, 2009
    Posts:
    263
    @Marrrk,

    Thanks for the reply, could you please give an example of say a REST call/response with the WWW object? I have it working fine with coroutines as a web player, but I am trying to switch to threading to take advantage of multi-core.

    This is what I have, it runs fine in the editor, but in web player it crashes soon as it starts.


    Code (csharp):
    1. class ThreadHelperServiceAsync : IServiceAsync
    2.     {
    3.         public void SendRequest(string url, JSONHandler callback, string serviceCall)
    4.         {
    5.  
    6.            var task = ThinksquirrelSoftware.Common.Threading.ThreadUtility.TaskDistributor.Dispatch(() =>
    7.                 {
    8.                      ThinksquirrelSoftware.Common.Threading.ThreadUtility.Dispatcher.Dispatch(() =>
    9.                         {
    10.                             WWW www = new WWW(url + "/" + serviceCall);
    11.  
    12.                             while (!www.isDone)
    13.                             {
    14.                             }
    15.  
    16.                             if (callback != null)
    17.                                 callback(www.text);
    18.                         });
    19.                 });
    20.         }
    21.     }
    22.  
     
    Last edited: Oct 17, 2012
  49. Marrrk

    Marrrk

    Joined:
    Mar 21, 2011
    Posts:
    1,032
    Hi AlexZ, I think using the Threading Helper for this is a little bit of an overkill as the www class already uses threads to download stuff, but if you want to use it here we go:

    Code (csharp):
    1.  
    2. class ThreadHelperServiceAsync : IServiceAsync
    3. {
    4.     public void SendRequest(string url, JSONHandler callback, string serviceCall)
    5.     {
    6.  
    7.        var task = ThinksquirrelSoftware.Common.Threading.ThreadUtility.TaskDistributor.Dispatch(() =>
    8.         {
    9.             WWW www;
    10.             ThinksquirrelSoftware.Common.Threading.ThreadUtility.Dispatcher.Dispatch(() =>
    11.             {
    12.                 www = new WWW(url + "/" + serviceCall);
    13.             });
    14.                
    15.             boo allowRun = true;
    16.             while(allowRun)
    17.             {
    18.                 ThinksquirrelSoftware.Common.Threading.ThreadUtility.Dispatcher.Dispatch(() =>
    19.                 {
    20.                     allowRun = !www.isDone;
    21.                 }).Wait();
    22.                
    23.                 // maybe add a thread.sleep here
    24.             }
    25.                
    26.             if (callback != null)
    27.             {
    28.                 ThinksquirrelSoftware.Common.Threading.ThreadUtility.Dispatcher.Dispatch(() =>
    29.                 {
    30.                     callback(www.text);
    31.                 });
    32.             }
    33.         });
    34.     }
    35. }
    36.  
    When you do not want to use the www class you could try to this:
    http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest(v=vs.90).aspx
     
  50. AlexZ

    AlexZ

    Joined:
    Sep 17, 2009
    Posts:
    263
    Hi Marrrk,

    Thank you very much for demonstrating how to get it working. I see your point about www, and so I tried to get it working with http://msdn.microsoft.com/en-us/libr...=vs.90).aspx

    I ran into a problem with it working on the web. the main thread seems to just die, i threw in some debugs and they stop shortly after the HttpWebRequest calls. Any ideas? Does it not work on web and only stand alone?