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

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

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

  1. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242

    Easy threading v3.0
    Link to the store: Easy threading

    Now including Thread Pools!

    Work with multiple threads easily!

    To build a truly responsive Unity game, you must keep long-running operations off of the main thread, and be careful to avoid blocking the main thread. This means you will need to execute various operations in the background.

    Unity's coroutines are ALWAYS executed on the main Thread

    Executing threads in the background implies carefully go back to the main thread when you have to manipulate Unity's objects.

    Piece of cake with this package, just use this sentence:

    Code (CSharp):
    1.  
    2. Task.RunInMainThread(SomeFunction);
    3.  
    SomeFunction is guaranteed to be executed in the main thread

    Of course i's possible to use lambda expressions:

    Code (CSharp):
    1.  
    2. Task.RunInMainThread(()=>
    3. {
    4.    // This code will execute in the main thread
    5. );
    6.  
    But this asset also makes working with threads a pleasant experience:

    To create a thread just use the following syntax:

    Code (CSharp):
    1.  
    2. Task.Run (DoSomeWorkOnABackgroundThread);
    3.  
    Creating a thread that returns some value (an integer in this example, but it can be any type, including a custom class):

    Code (CSharp):
    1.  
    2. Task<int>.Run (CalculateSomeIntegerInTheBackground);
    3.  
    Of course you will want to receive the integer:

    Code (CSharp):
    1.  
    2. Task<int>.Run (CalculateSomeIntegerInTheBackground).ContinueWith(ThisFunctionReceivesTheIntegerAsParameter);
    3.  
    Or, maybe, you need to show that integer to the player (the function receiving it must be executed in the main thread):

    Code (CSharp):
    1.  
    2. Task<int>.Run (CalculateSomeIntegerInTheBackground).ContinueInMainThreadWith(CheckTaskResult);
    3.  
    The function in "ContiuneWith" or "ContinuneInMainThreadWith" will receive the Task object and will be able to check its final state of the task (Succeeded, Aborted or Faulted) and its result (in case of tasks with parameters):

    Code (CSharp):
    1.  
    2. void CheckTaskResult(Task<int> t)
    3. {
    4.     if (t.IsFaulted)
    5.     {
    6.         log.text+= "<color=#550000>Error executing task: " + t.Exception + "</color>\n";
    7.     }
    8.     else
    9.     {
    10.         log.text+= "<color=#005500>Tasks executed successfully, result: " + t.Result + "</color>\n";
    11.     }
    12. }
    13.  
    It is possible to abort the execution of a task by invoking the method task.AbortThread, it will raise the Exception System.Threading.ThreadAbortException on the task code and set the task final state to Aborted:

    Code (CSharp):
    1.  
    2. m_taskToAbort = Task.Run (() => {
    3.     try
    4.     {
    5.         // [...] Code that supports to be aborted
    6.     }
    7.     catch(System.Threading.ThreadAbortException tae)
    8.     {
    9.         Debug.Log ("Thread was aborted before finishing");
    10.         throw tae; // rethrow if you want the task to be set as aborted
    11.     }
    12. });
    13. m_taskToAbort.ContinueInMainThreadWith ((t) => {
    14.     if(t.IsAborted)
    15.         log.text+= "Thread was aborted before finishing\n";
    16.     else
    17.         log.text+= "Thread finished without being aborted\n";
    18. });
    19. // [...]
    20. m_taskToAbort.AbortThread();
    21.  
    It is also possible to wait for a task to finish by blocking the current thread:
    Code (CSharp):
    1.  
    2. Task.Run (() => {
    3.     // [...] Code to be executed in auxiliary thread, and launches a second auxiliary thread
    4.     Task t = Task.Run (() => {
    5.         // [...] Code to be executed in auxiliary thread
    6.         Debug.Log("Task is finished, parent thread will be unblocked");
    7.     });
    8.  
    9.     // [...] And waits for that thread to finish
    10.     Debug.Log("Will wait for task");
    11.     t.Wait ();
    12.     Debug.Log("Task finished execution");
    13. });
    14. [/code=CSharp]
    15.  
    16. This package also offers to useful features extra to manage lists or pools of threads:
    17.  
    18. [B]Task.WhenAll[/B]: Creates a task that waits for all the thread to finish (for example waits to receive a confirmation from all the connected clients)
    19. [code=CSharp]
    20. // this task will wait for all task to finish
    21. Task.WhenAll (tasks).ContinueInMainThreadWith (DoSomethingWithTheResult);
    22.  
    Task.WhenAny: Creates a task that waits for the execution of one thread in the pool (for example if the game needs to get some data from the internet, and this data is available in multiple server, and it doesn't matter from where it comes, as long as the data is obtained from one server the game doesn't need to wait for the other servers to reply)
    Code (CSharp):
    1.  
    2. Task.WhenAny (tasks).ContinueInMainThreadWith ((t) => {
    3.     t.AbortThread(); // to cancel the remaining threads
    4.     DoSomethingWithTheResult(t);
    5. });
    6.  
    A more complicated, real life example: get the full contents of a paginated leaderboard from a server... but we don't want to wait to have the full board ready, we want to show the result as we get them:
    Code (CSharp):
    1.  
    2. bool m_stopProcessing;
    3. Task GetCompleteLeaderboard(Action<int, string> intermediateResult)
    4. {
    5.     TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool> ();
    6.     GetCompleteLeaderboardAux (0, tcs, intermediateResult);
    7.     return tcs.Task;
    8. }
    9. void GetCompleteLeaderboardAux(int offset, TaskCompletionSource<bool> tcs, Action<int, string> intermediateResult)
    10. {
    11.     if (m_stopProcessing)
    12.     {
    13.         m_stopProcessing= false;
    14.         tcs.SetError(new Exception("User cancelled"));
    15.     }
    16.     else
    17.     {
    18.         StartCoroutine(ConnectToAPI(offset, tcs, intermediateResult));
    19.     }
    20. }
    21. const int queryLimit= 10;
    22. IEnumerator ConnectToAPI(int offset, TaskCompletionSource<bool> tcs, Action<int, string> intermediateResult)
    23. {
    24.     yield return StartCoroutine(YOUR_FAVORITE_API_GetLeaderBoardUsingWWW (offset, queryLimit));
    25.  
    26.     SortedList<int, string> result = YOUR_FAVORITE_API_GetResultsFromCall ();
    27.  
    28.     if(result== null)
    29.     {    // mockup an error in the API
    30.         tcs.SetError(new Exception("API returns NULL"));
    31.     }
    32.  
    33.     foreach(int k in result.Keys)
    34.     {
    35.         intermediateResult(k, result[k]);
    36.     }
    37.     if (result.Count == queryLimit)
    38.     {
    39.         GetCompleteLeaderboardAux(offset + queryLimit, tcs, intermediateResult);
    40.     }
    41.     else
    42.     {
    43.         tcs.SetResult(true);
    44.     }
    45. }
    46.  
    This package reimplements and extends the .NET System.Threading.Tasks namespace.

    This namespace makes thread management easier for you by adding a class named Task. A task represents an asynchronous operation. Typically, a Task is returned from an asynchronous function and gives the ability to continue processing the result of the task.
    A task is not tied to a particular threading model: it represents the work being done, not where it is executing. Tasks have many advantages over other methods of asynchronous programming such as callbacks and the event model.

    You can learn more about tasks in Parse's website

    Tested for standalone builds, web player, WebGL, iOS and Android

    Full source code is included, as well as many examples. I use this asset for my own projects, so I will keep improving and expanding it.

    If you have any comment, doubt, feature request or just need some help implementing some multithreading behaviour just let me know here and I will try to help you out
    _____________________________________________________________________

    Now it includes Thread Pool class to create pools of threads (Tasks are managed by their own pool or you can specify a custom thread pool when you create them)

    Code (csharp):
    1.  
    2.     /// <summary>
    3.     /// Provides a pool of threads that can be used to execute tasks, post work items, process
    4.     /// asynchronous I/O, wait on behalf of other threads, and process timers.
    5.     /// </summary>
    6.     public class ThreadPool
    7.     {
    8.         /// <summary>
    9.         /// The number of requests to the thread pool that can be active concurrently.
    10.         /// All requests above that number remain queued until thread pool threads become available.
    11.         /// </summary>
    12.         public int maxThreads = 1;
    13.         /// <summary>
    14.         /// The difference between the maximum number of thread pool threads returned by the
    15.         /// GetMaxThreads method, and the number currently active.
    16.         /// </summary>
    17.         public int availableThreads
    18.         {
    19.             get;
    20.         }
    21.  
    22.         /// <summary>
    23.         /// Describes a pooled thread
    24.         /// </summary>
    25.         public class PooledThread
    26.         {
    27.             /// <summary>
    28.             /// Gets a value indicating whether this <see cref="U3D.Threading.ThreadPool+PooledThread"/> is enqueued.
    29.             /// </summary>
    30.             /// <value><c>true</c> if is enqueued; otherwise, <c>false</c>.</value>
    31.             public bool isEnqueued
    32.             {
    33.                 get;
    34.             }
    35.             /// <summary>
    36.             /// Aborts the pooled thread
    37.             /// (The thread may not be executing, and it won't receive the ThreadAborted exception)
    38.             /// </summary>
    39.             public void Abort();
    40.         }
    41.  
    42.         /// <summary>
    43.         /// Queues a method for execution. The method executes when a thread pool thread becomes available.
    44.         /// </summary>
    45.         /// <returns>PooledThread object describing the thread and its status.</returns>
    46.         /// <param name="a">The action to be executed in the thread.</param>
    47.         public PooledThread QueueAction(Action a);
    48.     }
    49.  
     
    Last edited: Jan 20, 2016
    AlanMattano, silvematt and rakkarage like this.
  2. aidesigner

    aidesigner

    Joined:
    May 2, 2012
    Posts:
    121
    I am reviewing all the threading options on the Asset Store. I do believe your solution is the best and like that it leverages programmers knowledge of .NET Tasks (Parse.Unity.dll). I would definitely purchase this asset as it often leads to more features/support/longevity. I do have some concerns/questions before moving forward.

    -Why did you create U3D.Threading.dll instead of U3D *.cs scripts. The latter would enable source access for debug, inspection, and modification.

    -Alternatively would you consider providing source for U3D somehow (GitHub or Email request)?

    -Did Parse.com intend for their Task library to be used standalone? Is there anyway to reduce the Parse.Unity.dll size for now by only including the System.Threading.Tasks namespace items? Does Parse provide source for there implementation of the System.Threading.Tasks namespace?

    I am somewhat apprehensive these days about dlls. This is because it prevents customization and full maintenance if the asset support vanishes.

    Thanks,
    aidesigner
     
    Last edited: Oct 1, 2014
  3. arklay_corp

    arklay_corp

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

    Thanks for your words, I really appreciate it! I will keep this asset free for the moment, since it's very simple and most of it functionality relies on parse's dll. If it starts to grow, or the support takes too much time I will consider start selling it.

    About your questions:

    - I created the .dll because i was researching how to publish assets and it was kind of a test, but you are completely right and in the next version I will provide full source code (it's quite simple, tho)

    - I will provide it in the next version, but if you are interested i can either send it to you, post it here, or give you access to the bitbucket repository

    - I'm not sure what parse intended and I didn't find a way to reduce the .dll size, I tried other solutions (using mono's dll, etc...) but IMO parse's is the best implementation, and the code overhead is worth it.

    You are welcome.
     
  4. charmandermon

    charmandermon

    Joined:
    Dec 4, 2011
    Posts:
    352
    Doesn't work in the latest 4.6 f3. Too bad I needed this magic today.
     
  5. arklay_corp

    arklay_corp

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

    I'm sorry it doesn't work. I cannot update to 4.6.f3 while in beta version, but if you give me more information maybe I can help you to fix it.
     
  6. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    I updated the implementation, I removed parse's dll to avoid code overhead, now I use a custom implementation for U3D.Threading.Tasks

    Now the asset is not that simple, so it isn't free anymore... but is as cheap as it can get.
     
    Last edited: Jan 10, 2015
  7. kilik128

    kilik128

    Joined:
    Jul 15, 2013
    Posts:
    909
    1. Dispatcher.instance.ToMainThread(()=> {
    2. // Code here will execute in the main thread
    3. });
    only that's ?
     
  8. kilik128

    kilik128

    Joined:
    Jul 15, 2013
    Posts:
    909
    so first try do that's

    Dispatcher.instance.ToMainThread(()=> {
    a = combine ();
    });

    it's work's see lot difference on profiler but result it's completly change


    we must use it's line before starting ?
    System.Threading.Thread t= new System.Threading.Thread(new System.Threading.ThreadStart(ExecuteTests));

    or just Dispatcher.Initialize();
     
  9. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Yes, that's all u need

    I dont get what u want to achieve, Dispatcher.Initialize won't start any thread..

    (sorry if post is messy, I'm writing from my mobile phone)
     
  10. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    I keep improving this asset with real life situations I face in real life since I use it in all my projects, in this latest update I extended the Task class to include two new methods:
    Code (CSharp):
    1.  
    2. /// Queues the specified work to run on the ThreadPool and returns a Task handle for that work, the action is guaranteed to be executed in the main thread
    3. public static Task RunInMainThread(Action action)
    4.  
    Code (CSharp):
    1.  
    2. /// Creates a continuation task that executes on the Main thread when the target <see cref="Task"/> completes.
    3. public Task ContinueInMainThreadWith(Action<Task> continuationAction)
    4.  
    Now the package includes an almost-real-life example: how to get a leaderboard from a remote server when the server will send the leaderboard paginated and we want to show it as soon as we get the results:
    Code (CSharp):
    1.  
    2.             GetCompleteLeaderboard(
    3.                 (pos, name) =>
    4.                 {   // this action will be executed as soon as we get every row of the leaderboard
    5.                     WriteLog("{0} - {1}", pos, name);
    6.                 }
    7.                 ).ContinueInMainThreadWith((t) =>
    8.                 {  // here we can control the result of the request
    9.                    if(t.IsFaulted)
    10.                    {
    11.                        Exception e = t.Exception.InnerExceptions[0];
    12.                        while (e is U3D.AggregateException) e = ((U3D.AggregateException)e).InnerExceptions[0];                  
    13.                        WriteLog("--- Error getting leaderboard {0}", e.Message);
    14.                    }
    15.                    else
    16.                    {
    17.                        WriteLog("--- Finished getting leaderboard");
    18.                    }
    19.                });
    20.  
     
    AlanMattano likes this.
  11. sxmad

    sxmad

    Joined:
    Nov 4, 2014
    Posts:
    6
    Hi,arklay_corp
    I make multi thread for my socket receive. and when get data form server, invoke Dispatcher.instance.ToMainThread to show my data on UGUI. there have some question.
    1. if in one scene ,works good.
    2. if switch scene from A to B, works good. and then switch to A agin, the call back function Dispatcher.instance.ToMainThread can not work. like this:
    Debug.Log("work good"); // print "work good"
    Dispatcher.instance.ToMainThread(()=> {
    Debug.Log("work good?"); // print nothing
    });
    3. then I add a function to Dispatcher.cs:
    public void Reset(){
    _initialized = false;
    m_q.Clear();
    }
    and use this before switch scene. it works. why?
     
  12. arklay_corp

    arklay_corp

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

    As you can see in the code, the function public static void Initialize() instantiatiates a singleton instance for the Dispatcher, this instance is marked as DontDestroyOnLoad and will persist between scene changes. Since you are resetting the _initialized flag you will end up with an extra instance of the Dispatcher per scene change... Which is not what you want.

    The dispatcher should work between scene changes, I would need a project example to check what's wrong (you can send me a private message if you don't want to post it here).

    Also make sure you are not trying to call an action that uses an object that doesn't exists anymore.
     
  13. overthrowrobotics

    overthrowrobotics

    Joined:
    Jun 19, 2015
    Posts:
    47
    I've been using the standard .NET threading library but have problems with hanging threads so I purchased your module.

    I want to start a new thread and pass a string to it but am having trouble figuring out how to do it with this module.

    This is how I'm doing it now.
    Code (csharp):
    1.  
    2. Thread threadMove = new Thread (() => Gesture_Move_Thread (gestureName));
    3.  threadMove.Start ();
    4.  
    5. private void Gesture_Move_Thread (string gestureName)
    6.     {
    7. // do stuff
    8. }
    9.  
     
  14. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    The most straightforward way to accomplish the same would be:
    Code (csharp):
    1.  
    2. string gesture= "Something meaningful";
    3. Task.Run (() =>
    4. {
    5.    Gesture_Move_Thread (gestureName);
    6. });
    7.  
    8. private void Gesture_Move_Thread (string gestureName)
    9. {
    10.    // do stuff
    11. }
    12.  
    Check Examples.cs in the examples folder for more options, such as returning some value and/or return the execution to the main thread. Specifically the CreateThread and CreateThreadReturningValue methods
     
  15. sheng319

    sheng319

    Joined:
    Jun 23, 2014
    Posts:
    34
    Does it have thread pool?
     
  16. Dan2013

    Dan2013

    Joined:
    May 24, 2013
    Posts:
    200
    @arklay_corp

    Seems very interesting.

    I am new to multi-threading of Unity.
    What is the typical usage scenario of multi-threading in Unity, since Unity's APIs are not thread safe.
    I guess we may put some independent expensive calculations into some background thread (Task here)? Like AI calculations? Or, multi-threading is useful for some heavy IO scenarios like socket or file IO (maybe not useful)?

    Another question, you reimplements the official "System.Threading.Tasks" here because "System.Threading.Tasks" requires new NET (with version > 4.5) and Unity's Mono is too old?
     
  17. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    @sheng319 no, it doesn't, you can use continuation methods to reuse the thread, but i never met a scenario working with unity that required a thread pool (usually games are not that thread intensive). If you describe me a plausible scenario where threading pool is useful I will give it a go.

    @Dan2013 you are right in all your assumptions, I use it mostly for networking operations, this plugin is quite useful to send and detach the execution to and from the main thread. Exactly, System.Threading.Tasks is not available in unity's imlpementation of .Net
     
  18. sheng319

    sheng319

    Joined:
    Jun 23, 2014
    Posts:
    34
    Can use it in the editor environment?
     
  19. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    yes, you could, but you will need to modify some scripts and mark them to run in editor environment (Dispatcher.cs and maybe some others)
     
  20. Dan2013

    Dan2013

    Joined:
    May 24, 2013
    Posts:
    200
    @arklay_corp
    Awesome. This is what I want. I've already purchased it.

    When using it to do networking operations, you typically use Socket or UnityEngine.Networking?
    What is the typical appropriate way to do networking operations with this asset?
    Maybe you can put some more detailed sample code fort this topic. :)
     
    Last edited: Sep 9, 2015
  21. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    I use parse.com as backend so most of my communication is made through their API

    Between Unity networking (WWW) and sockets... it depends on you needs, your experience with each and your taste....

    Take a look at LeaderboardTest.cs, it has a good example simulating getting a leaderboard from a backend service (of course the connection to the backend is mocked)
     
  22. Dan2013

    Dan2013

    Joined:
    May 24, 2013
    Posts:
    200
    I see. Thanks.

    I think my game will use both RESTful and my own protocols (for real-time part).
    Unity update its official networking components recently. Its new Unity Transport API is some "thin layer over UDP". It sounds like an reasonable solution for real-time gaming. I am not sure if it is mature. That is why I ask the question above to see if you used it with Task before.
     
    Last edited: Sep 9, 2015
  23. CapeGuyBen

    CapeGuyBen

    Joined:
    Mar 1, 2014
    Posts:
    7
    I'm using EasyThreading in my current project and think it's a great asset. Makes using Tasks in Unity easy and the asset is also surprisingly simple (which is a good thing!).

    I have noticed that in version 3.1 of the asset there is an unused private variable "Func<TResult> m_funciton" in Task<TResult> which is causing errors in my project (I have warnings as errors enabled). Not a big deal as I've just removed the variable locally but thought you might want to take it out before your next release.
     
    arklay_corp likes this.
  24. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    @Dan2013 I didn't use unity networking yet, I'm a happy photon and smartfox server user.... maybe in the next project I start from scratch I will give it a chance

    @Benakin486 thanks a lot!

    yeah I noticed about that variable and I'm sorry it gave you some problems, I have it removed and it will be fixed in the next version, I just don't want to upload a new version just for that (some users are complaining about publishers updating assets too often to gain visibility, I don't want to be seen as a cheater)
     
  25. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I don't think that's ever a real problem. More uploads are always better. It does not gain visibility like sales gain visibility.
     
  26. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Oh! I read it somewhere... ok I will upload a new version today. Thanks @hippocoder

    Ok, new version uploaded, in a couple of weeks it will be solved
     
    Last edited: Sep 11, 2015
  27. Lizzard4000

    Lizzard4000

    Joined:
    Mar 3, 2010
    Posts:
    101
    Hi!

    I'm using Easy Threading with Unityscript and is works well.
    But how would i write the above in Unityscript? How to pass a var?

    Thanks!
     
  28. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Hi!
    I don't use Unityscript but my guess would be:
    Code (JavaScript):
    1. var gesture= "Something meaningful";
    2. Task.Run (function ()  {
    3.    Gesture_Move_Thread (gestureName);
    4. });
    5. function Gesture_Move_Thread (gestureName)
    6. {
    7.    // do stuff
    8. }
    I cannot try it right now, let me know if it workd
     
  29. Lizzard4000

    Lizzard4000

    Joined:
    Mar 3, 2010
    Posts:
    101
    Works perfectly! Thanks a lot for your fast help!
     
  30. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    no problem ;)
     
  31. nirvine_bns

    nirvine_bns

    Joined:
    Sep 23, 2015
    Posts:
    2
    Bug report: I'm pretty sure Wait() is broken and will always throw its WTF exception.

    Wait waits on _syncEvent semaphore, and then if the state is still not completed, throws an Exception.

    In m_state.set, you call _syncEvent.Set() and then change the state. Since the waiting thread call to WaitOne() returns after Set() is called but before __state is updated, it will not have been updated yet and IsCompleted will remain false.
     
  32. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Good catch nirvine_bns!
    I will upload a new version fixing this issue. Actually it's a race condition, if the executing thread change happens right after _syncEvent.Set() you would get the WTF exception.

    Until the new version gets to the store please just add the following line right before _syncEvent.Set(); (line 37 of Task.cs):

    Code (CSharp):
    1.                            
    2.                         case TState.Faulted:
    3.                                 if (__state != TState.Running)
    4.                                     throw new InvalidOperationException(string.Format("Invalid state transition; {0} -> {1}", __state, value));
    5.                                 __state = value;  //new line
    6.                                 _syncEvent.Set();
    7.                                 break;
    8.  
    Thanks nirvine_bns!
     
  33. MaestroMMT

    MaestroMMT

    Joined:
    May 31, 2015
    Posts:
    26
    Hello and thanks for your great job, I am using it in all my projects.

    Unfortunately, I can't build it in windows phone 8.1 / Universal. I know it might be possible to use #if NETFX_CORE and use another thread implementation, but that would imply to change a lot of my project's code. Do you have any idea or a workaround ?

    Thanks in advance :D
     
  34. Leviathan1753

    Leviathan1753

    Joined:
    Feb 15, 2014
    Posts:
    13
    Heya Arklay,

    First off, thanks for the library! Helps stave off my hunger for true .NET tasks while Unity gets their stuff together, haha.

    I had a question about thread spawning/pooling with your library. Is a thread spawned for every task? If so, have you experienced problems with running lots of small tasks, or does it handle it okay in your experience?

    Something like:
    List<Task> tasks = new List<Task>();
    foreach (int i = 0 ; i < 10000 ; i++)
    {
    tasks.Add(Task.Run(() => SomethingUsuallySmallButOccasionallyLong());
    }
    Task.WhenAll(tasks).Wait();

    This would spawn 10k threads, no? I feel like that would bog down the system more than having a smart thread pool that expanded/contracted as the tasks-to-do changed. I was going to test this myself in the coming days, but thought you might have some input/experience with it as well.

    Just for some background of why I might spawn this many tasks/threads:
    I feel like in addition to offloading of a few large workloads to an alternate thread, Tasks are also extremely useful for coordinating completion of many small items too. I might start 10k small jobs, most of which take very small amounts of time or finish instantly, but others might take much longer, and we don't know ahead of time. In real .NET tasks, this isn't a problem, as tasks that never go async stay single threaded, and the ones that do get thrown into a thread pool, rather than actually spawning a thread per task. However, I'm a bit wary of following the same patterns here in this library.

    Anyway, it might turn out fine for all I know, just starting the discussion and getting your thoughts.

    Cheers!
    - Lev


    EDIT: Some more thoughts. It might be dangerous to switch to a thread pool option with this task system, given that it uses Wait()s vs true async/await systems where threads are released upon waiting. No fault of yours there, as we're using old .NET, haha. I could imagine a thread pool getting deadlocked on a selection of tasks all of which are waiting for a task that's in the queue for the pool. Those tasks won't complete until the later one, but the later one won't be queued until slots open up. So perhaps it's necessary to have a thread per task, unless we have a super fancy pool.
     
    Last edited: Nov 16, 2015
  35. nirvine_bns

    nirvine_bns

    Joined:
    Sep 23, 2015
    Posts:
    2
    No problem. Thanks for the quick response :)
     
  36. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Hi Maestro!
    First thanks a lot for your compliments, it means a lot :)

    About your problem, sorry but I don't understand completely what do you mean (I'm no expert in WP8.1) I can build the examples for WP8 and execute them without problems... Are you having problems with collisions between .NET Task classes and my plugin classes?
     
  37. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Hey Leviathan1753,

    Apparently including a thread pool is getting popular in this thread :D Yes, you are right, if you create thousands of threads at the same time you will have problems: not only the performance will be terrible but your game will end up crashing (there is a limit on the number of threads, it depends on the platform, but is not very high)

    As I said I didn't do it (yet) because I never encountered a game that is that thread intensive.... Implementing a thread pool and tweak the plugin to reduce threads usage will require to change the approach to it, I cannot promise anything but when I will have some spare time I will give it a thought.

    Cheers!
     
  38. Leviathan1753

    Leviathan1753

    Joined:
    Feb 15, 2014
    Posts:
    13
    Heya Arklay,

    No worries, I know how support can get haha. I was more just bouncing the ideas off to see if I was thinking correctly or if there was something going on I wasn't aware of. I'll probably implement some changes myself and see how it goes. If I get anything fancy I'll let you know.
     
    AlanMattano likes this.
  39. MaestroMMT

    MaestroMMT

    Joined:
    May 31, 2015
    Posts:
    26
    Thanks for your answer.

    Indeed, when I want to build in wp8.1/Universal, I am getting the following errors :

    Assets\Plugins\U3D\Threading\Tasks\Task.cs(164,3): error CS0246: Le type ou le nom d'espace de noms 'Thread' est introuvable

    Assets\Plugins\U3D\Threading\Tasks\Task.cs(16,9): error CS0246: Le type ou le nom d'espace de noms 'ManualResetEvent' est introuvable

    Thread library from .NET is not found.. I have this issue only for wp8.1/universal build. I tried to change compilation Overrides in player settings, but without success.. :(

    I don't understand why it's works on your project o_O. Do you have any idea why I am an exception ? :p

    Thanks a lot ^^
     
  40. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    A few questions..

    Does your system implement a thread pool where the threads are preallocated and can be re-used, so as to avoid overhead of creating new threads? If I need to use e.g. 8 threads every frame I don't want to keep creating and destroying them.

    Is it possible to easily launch several threads and to then wait until all of them are finished their tasks? e.g. they all do chunks of background processing and then I need a final `all done` signal.

    Is it possible to launch several threads and then wait ie pause the main thread until the threads are all done without having to manually check every one in a loop or whatever?

    Is it possible to add a lock on some portion of data or memory so that the other threads cannot modify the same data until the lock is released?
     
  41. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    I think you are getting namespaces collisions, do not import System.Threading and U3D.Threading (rather use the fully qualified names System.Threading.* and U3.Threading.Task)
     
  42. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    As stated before, the system doesn't implement a thread pool because I never met any project that thread intensive for it to make a difference (but I promise I will give it a thought when I will have some spare time).

    But 8 threads it's nothing to worry about and you don't need to create and destroy them, the library takes care of it, that's its all point
    Yeah, there is Task.WhenAll that takes a list of tasks (threads) and returns a Tasks that includes all of them (then you can just use the Task method ContinueWith to execute some code when all the tasks are finished (there is also a Task.WhenAny that wait just for 1 of the threads to finish without error, in case you want, for example, to try to connect to a bunch of sites, but only need to keep the first connection that worked)
    Yes, the task object has a Wait() blocking method
    You can use any mutex, semaphore, monitor provided by .NET, the easiest being:
    Code (CSharp):
    1. ...
    2. lock(syncObject)
    3. {
    4.    // shared data access
    5. }
    6. ...
     
  43. MaestroMMT

    MaestroMMT

    Joined:
    May 31, 2015
    Posts:
    26
    This issues comes from U3D.Threading.Tasks.cs where System.Threading is needed. I tried to change all namespaces with system.Threading.* format. It still doesn't work. Thread class / namespace is always not found.

    Drive me crazy :D
     
  44. MaestroMMT

    MaestroMMT

    Joined:
    May 31, 2015
    Posts:
    26
    It's now working on windows phone 8.0, but it still the same issue in windows phone 8.1 / universal 8.1 / windows store build settings.

    I had to change some dll, like newtonsoft and system.data dll for windows phone compability.

    In 8.1, Thread namespace/class is still not found.
     
  45. Leviathan1753

    Leviathan1753

    Joined:
    Feb 15, 2014
    Posts:
    13
    Heya Arkley,

    Been messing around and trying to integrate it with our current systems. Got some more feedback. First off, I do think there's too many threads roaming around internally for all the wiring. Things like ContinueWith spawn a whole thread just to pass off tasks. Maybe that's not a huge deal for doing big jobs every so often, but I do think it'll pile up for anything intensive.

    The other issue is the lack of the "await" keyword, which I know isn't really your fault, haha. In real .NET, I believe you're really supposed to avoid using .Wait(), as that's somewhat a crutch API call. It's supposed to be "async all the way down" from what I've read, at least.

    This got me thinking about leveraging coroutines concepts a bit. Those are actually the closest thing in Unity to having the await keyword. Rather than await, you just yield return, which looks and feels (and almost acts) like await.

    In a similar fashion, it would be awesome to be able to make Tasks that ran in the background threads that could use the same yield return (await) pattern. I sparingly use ContinueWith in my real .NET code, but I'm forced to exclusively use them here.

    For example, I might have some code using the current Task library like:
    Code (CSharp):
    1.  
    2. // Variable 1
    3. // Variable 2
    4. Task.Run(() =>
    5. {
    6.     // some code
    7.     // set variable 1
    8. })
    9. .ContinueWithOnMainThread(SomeCode)
    10. .ContinueWith((t) =>
    11. {
    12.    // Some more code
    13.    // Set variable 2
    14. }
    15. .ContinueWithOnMainThread(SomeCode2)
    16. .ContinueWith((t) =>
    17. {
    18.     // Wrap up using variable 1 and 2
    19. }
    20.  
    A good bit of wrapping -- I have to make sure to put my variables outside the tasks, so they can be used cross-task -- This whole setup spawns several (5?) threads.


    You could make a Task.Run(Func<IEnumerable<Task>> toDo) call, which would allow for something like:
    Code (CSharp):
    1.  
    2. Task.Run(() =>
    3. {
    4.     // some code
    5.     // Define + set variable 1
    6.     yield return Task.RunOnMainThread(() => SomeCode());
    7.     // Define + set variable 2
    8.     // Some more code
    9.     yield return Task.RunOnMainThread(() => SomeCode2());
    10.     // Wrap up using variable 1 and 2
    11. });
    12.  
    This is cleaner wrapping, don't need to worry about putting variables outside, it could be done in 1 background thread, etc.

    Anyway, I will be trying to implement some of these ideas on my end... see how it goes in reality.


    EDIT: Just realized you can't do lambdas that are enumerable iterators. So that code would have to point to an actual method that returned IEnumerable<Task>, unfortunately.
     
    Last edited: Nov 21, 2015
  46. arklay_corp

    arklay_corp

    Joined:
    Apr 23, 2014
    Posts:
    242
    Please try to detail the environment that generates the error (SO, target, other libraries...) I will try to reproduce it and help you out
     
  47. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    Bought this. Trying to use it mainly from javascript. Having trouble.... it seems not to be able to find anything to do with the `Task` type.. either `Task.Run` or `var a:Task=Task.Run` etc... "the name Task does not denote a valid type (not found)". I have moved the U3D folder inside the Plugins folder hoping it would work, but doesn't seem to. Tried "Standard Assets" subfolder also, no luck. I have no other changes to script execution order. Why can't the javascript see your Task scripts? Am I supposed to include something in the scene or make an instance of a task script or something? My unity script (javascript) is not inside any kind of class wrapper, it's just procedural code, ie doesn't have an 'extends monobehavior' thingie, does that matter?

    [Edit] I threw " import U3D.Threading.Tasks; " at the start of the javascript.... maybe works? right approach? [/edit]?

    [Edit] Seems this was what was needed, unless you have some better idea? ... got my script to compile and not throw errors. .... also running a function with parameters, it doesn't seem to like it... so I had to put the function call inside a new in-line function e.g.

    Tasks[ThreadCount]=Task.Run( function() {
    WipeSky( SkyPixelsDone,SkyPixelsDone+Mathf.Min(SkyPixelsTotal/8,SkyPixelsLeft) ); //Do~1/8thof work
    } );

    [/edit]
     
    Last edited: Dec 17, 2015
  48. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    Well once I managed to take out a couple of things that aren't allowed in threads, like Random.value, and accessing the dimensions of a texture, I got a function running. It runs on 8 threads on a 4-core hyperthreaded i7, and used to take 17 seconds. It now takes 4 seconds. Yay :-D Best $8 ever spent.
     
    ilmario and DrKucho like this.
  49. arklay_corp

    arklay_corp

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

    Sorry for the late response, I really appreciate that you are happy with your investment :)
     
  50. arklay_corp

    arklay_corp

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

    I finally had some time and added thread pools. I also improved the thread usage, now each task creates 1 thread for its whole life, including continuation actions.

    Note that the wait method has changed and now is not blocking and acts in a similar way as a continuation action.

    I hope you will find useful all this changes.

    As you can see I raised the price after including this new features but, of course, whoever had the asset before doesn't need to pay more ;)

    Also my current project uses this asset and it will be released for WebGL, meaning I will have to test it through for this platfom, I will keep you posted
     
    Leviathan1753 and zyzyx like this.