Search Unity

Free multi-threading framework in C#: Executors Framework

Discussion in 'Made With Unity' started by magwo, Aug 12, 2010.

  1. magwo

    magwo

    Joined:
    May 20, 2009
    Posts:
    402
    I've implemented a small framework for multi-threading applications in a very clear way. It's called "Executors Framework" and is inspired by similar constructs that are available in the Java standard library.

    http://www.unifycommunity.com/wiki/index.php?title=Executors_Framework


    Comments and feedback very welcome! Especially if you spot bugs or potential threading unsafeties. When it appears solid enough, I will link to the page from the Scripts list.

    I couldn't find anything like this in the .NET libraries, which is why I implemented it. Feel free to mention if this already exists in .NET, and I will put on the fool hat.
    Code (csharp):
    1. <:)
    Edit: I should mention that this is for advanced users only - do not attempt to introduce multi-threading in your regular Unity code. The primary uses for this framework is when you are doing heavy isolated computations, or are interfacing with external entities/libs that have blocking calls or long return times.
     
  2. Tinus

    Tinus

    Joined:
    Apr 6, 2009
    Posts:
    437
    Cool stuff! I'm not very familiar with C#'s threading libraries, so I can't tell you if there's something similar in there. All I know is that I really like this way of working in Java, and am happy to see it usable withing Unity. :)
     
  3. DavidB

    DavidB

    Joined:
    Dec 13, 2009
    Posts:
    530
    This looks really cool, can't wait to play around with it to see what it can do :D

    Cheers
     
  4. rahuxx

    rahuxx

    Joined:
    May 8, 2009
    Posts:
    537
    Does this make my application made in unity use multi-threading of my PC quad core processors.

    thanks
    rahu
     
  5. magwo

    magwo

    Joined:
    May 20, 2009
    Posts:
    402
    No, it's more complicated than that. If you don't know how to use this, you probably don't need it.
     
  6. Sbd

    Sbd

    Joined:
    Jun 16, 2008
    Posts:
    36
    Would it be possible to add callbacks (delegates) instead of polling for isDone?

    Code (csharp):
    1. Future<int> myFuture2 = myExecutor.Submit(new MultiplyIntsTask(5, 12), MyCallback);
    Where MyCallback would be called upon Future task completion.
     
  7. z00n

    z00n

    Joined:
    Nov 24, 2009
    Posts:
    44
    C# is not Java and you can do it much simpler (ICallable -> Delegate, Begin/EndInvoke etc.)

    For most cases Future<T> by Joe Duffy will suffice.
    Code (csharp):
    1.  
    2. public class Future<T> : IDisposable
    3. {
    4.  
    5.     // Fields
    6.     private readonly Func<T> _func;
    7.     private Exception _except;
    8.     private T _value;
    9.     private ManualResetEvent _mre;
    10.  
    11.     // Constructors
    12.     public Future(Func<T> func)
    13.     {
    14.         _func = func;
    15.         ThreadPool.QueueUserWorkItem(Worker);
    16.     }
    17.  
    18.     // Properties
    19.     public bool IsDone { get; private set; }
    20.  
    21.     public T Value
    22.     {
    23.         get
    24.         {
    25.             // If the future isn't completed, we wait.
    26.             if (!IsDone)
    27.             {
    28.                 _mre = new ManualResetEvent(false);
    29.                 Thread.MemoryBarrier();
    30.                 if (IsDone)
    31.                 {
    32.                     _mre.Close();
    33.                     _mre = null;
    34.                 }
    35.                 else
    36.                 {
    37.                     _mre.WaitOne();
    38.                 }
    39.             }
    40.  
    41.             // If an exception was thrown by the future, repropagate it.
    42.             if (_except != null)
    43.             {
    44.                 throw _except;
    45.             }
    46.  
    47.             return _value;
    48.         }
    49.     }
    50.  
    51.     // Methods
    52.     public void Dispose()
    53.     {
    54.         if (_mre != null)
    55.         {
    56.             _mre.Close();
    57.             _mre = null;
    58.         }
    59.     }
    60.  
    61.     private void Worker(object obj)
    62.     {
    63.         try
    64.         {
    65.             _value = _func();
    66.         }
    67.         catch (Exception e)
    68.         {
    69.             // Capture the exception. It will be propagated when the consumer
    70.             // rendezvous with the future.
    71.             _except = e;
    72.         }
    73.         finally
    74.         {
    75.             IsDone = true;
    76.             Thread.MemoryBarrier();
    77.             if (_mre != null)
    78.             {
    79.                 _mre.Set();
    80.             }
    81.         }
    82.     }
    83.  
    84. }
    85.  
     
  8. immortius

    immortius

    Joined:
    Aug 7, 2009
    Posts:
    41
    .Net has a BackgroundWorker class for this sort of thing - it is part of the Windows Forms system though, not sure if it can be used in Unity.
     
  9. magwo

    magwo

    Joined:
    May 20, 2009
    Posts:
    402

    Thanks for sharing, and welcome to the forums! I wasn't entirely aware of the capabilities of Begin/EndInvoke.

    However:
    1. There appears to be considerable performance hits for using delegates and begin/endinvoke. I think my approach avoids this.
    http://shiman.wordpress.com/2008/09/10/c-net-delegates-asynchronous-invocation-begininvoke-method/

    2. Begin/EndInvoke does not let you specify the manner in which the task gets processed. For me it's very nice to be able to change between threaded and non-threaded by just changing a single instantiation line.
     
  10. magwo

    magwo

    Joined:
    May 20, 2009
    Posts:
    402

    Yes, but it sort of defeats the purpose of the whole framework.. because the callback would be called from a foreign thread, which is the "can of worms" we're trying to avoid.
     
  11. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    can of worms is a nice word for it ;)
    Unity isn't threadsafe, on a real multi threading system chances are pretty realistic that it will bomb ...
    you can already see errors happening due to parallel www request to the same domain actually as the data loading for the data in the WWW object is async in unity 2.6.x too
     
  12. z00n

    z00n

    Joined:
    Nov 24, 2009
    Posts:
    44
    >>> Thanks for sharing, and welcome to the forums!
    Thank You!


    >>> 1. There appears to be considerable performance hits for using delegates and begin/endinvoke. I think my approach avoids this.

    Note that Joe's code does not use Begin/EndInvoke, but uses very fast MemoryBarrier and ManualResetEvent for signaling.
     
  13. magwo

    magwo

    Joined:
    May 20, 2009
    Posts:
    402

    I just got a nice idea for this - by introducing a Unity component class called ExecutionManager, we can do the polling in there and support the callback pattern that you requested. Will update the code soon.
     
  14. tylo

    tylo

    Joined:
    Dec 7, 2009
    Posts:
    154
    And that was the last we ever saw of DavidB. He starved to death trying to solve the Dining Philosopher's problem.
     
  15. magwo

    magwo

    Joined:
    May 20, 2009
    Posts:
    402
  16. magwo

    magwo

    Joined:
    May 20, 2009
    Posts:
    402
    Actually.. that code won't work in Unity (even 3.0) I think, because it uses .NET 3.5 classes. And I did try good old delegates, but ran into synchronization problems. It appears to not be thread-safe to use surrounding-scope variables in a delegate called by another thread:
    Code (csharp):
    1.  
    2.     for(int i = 1; i < 10; i++) {
    3.       futures.Add(executor.Submit<double>(delegate () { return i*i; } ));
    4.       expectedAnswers.Add(i * i);
    5.     }
    6.  
    Or could this be just a Unity/Mono bug in this regard? I kind of expected ints like these to be copied into some kind of anonymous invisible object for the delegate to refer to, which should be thread safe.
     
  17. llde_chris

    llde_chris

    Joined:
    Aug 13, 2009
    Posts:
    205
    ^ Article is from 2006, I doubt it uses .Net 3.5 classes :wink:
     
  18. magwo

    magwo

    Joined:
    May 20, 2009
    Posts:
    402
    Oh... my bad.. :)
    For some reason I thought it was using this:
    http://msdn.microsoft.com/en-us/library/bb534960.aspx

    Must have just been tired when I read it.


    Edit: Found out what the problem was with anonymous delegates and accessing outer scope:
    http://lorgonblog.spaces.live.com/Blog/cns!701679AD17B6D310!689.entry
     
  19. havchr

    havchr

    Joined:
    Jun 18, 2009
    Posts:
    75
    Thanks a lot for this! Was nice to use and set up, and helped me with throwing some md5-hash calculations on a thread, instead of stalling the app.

    Big thanks :)