Wednesday 8 October 2014

UniParallel - New features in version 0.6

Download it free from the Unity Asset Store

For Android, iOS, Windows Phone, Windows, MacOS, Linux and probably a whole set of other platforms that support .Net natively. Try it, it's free!

If you are new to UniParallel, please read the post Using PTaskScheduler - UniParallel Unity package first.

Version 0.6 of the UniParallel package adds a new type of task scheduler called  PSyncTaskScheduler  which has the same functionalities of the  PTaskScheduler  adding to it the ability to execute a routine in the main game Thread after a task is finished.
Only 3 of types of tasks are supported, named slightly different:

  • Use SyncImmediateTask if you want to schedule a task for immediate execution
  • Use SyncTimedTask if you want to schedule a task for delayed execution
  • Use SyncRepeatableTask to execute tasks a certain number of times on a interval

Infinite synchronized tasks are supported, obviously, since the purpose of the sync tasks is to execute another routine when finished.

And every starts with and instance of  PSyncTaskScheduler 
 [...]
 PSyncTaskScheduler scheduler = new PSyncTaskScheduler();
 // schedule your tasks
 [...]
 // check if there's any tasks finished and execute it's post completion routine
 scheduler.Update(); 

The line
scheduler.Update();
Is extremely important and must be included in your MonoBehaviour update loop. This call is responsible for executing and post-completion routine scheduled. That is, if there's any completed tasks. Calling PSyncTaskScheduler.Update() when there are no tasks completed or scheduled is completely safe and will cause no impact on your application performance whatsoever.
Let's see an example of a immediate task that will execute a heavy operation in the background and notify the game when completed
 
 [...]
 PSyncTaskScheduler scheduler = new PSyncTaskScheduler();
 SyncImmediateTask task = new SyncImmediateTask(MethodThatCanBlockMyGame, MethodToRunAfterBlockingMethodIsDone);
 scheduler.Enqueue(task);
 [...]

 // The MonoBehaviour update loop
 void Upate()
 {
    scheduler.Update()
 }
 object MethodThatCanBlockMyGame()
 {
    // this is running in the background and allowing your game to continue smoothly
    Boolean isDone = true;
    // Do a few very heavy operations
    ThisIsGoingToTakeALongTime();
    // isDone is the argument passed to MethodToRunAfterBlockingMethodIsDone
    return isDone;
 }
 
 void MethodToRunAfterBlockingMethodIsDone(object result)
 {
   // this is running inside the game's main thread and MUST BE very light-weight code
   Boolean booleanResult = (Boolean) result;  // result is coming from  MethodThatCanBlockMyGame
   if(result)
   {
     Debug.Log("Completed successfully");
   }
   else
   {
     Debug.Log("Ooops");
   }
 }

Note the signatures of the methods  MethodThatCanBlockMyGame  and  MethodToRunAfterBlockingMethodIsDone : they *must* work with only object types, the background method returning and object that will be passed as an argument to the method executed after the background routine is completed.
Let's take a look at the other 2 types of synchronized tasks. They are just as simple to use as immediate tasks. First, timed tasks
 
 [...]
 const int delay = 1000; // 1 second
 PSyncTaskScheduler scheduler = new PSyncTaskScheduler();
 SyncTimedTask timedTask = new SyncTimedTask(MethodToStartAfterDelay, MethodToExecuteAfterComplete, delay); 
 scheduler.Enqueue(timedTask);
 [...]

 // The MonoBehaviour update loop
 void Upate()
 {
    scheduler.Update()
 }
 object MethodToStartAfterDelay()
 {
    // this is running in the background, starting after the specified delay
    Integer calculationResult = DoSomeMath();
    return calculationResult;
 }
 
 void MethodToExecuteAfterComplete(object result)
 {   
   Debug.Log("The calculated number is "+result);   
 }

Then, SyncRepeatableTask tasks. This time of task is not only useful for executing heavy operations in background but also delayed repeatable operations that will result in something to be rendered, like a countdown timer
 
 [...]

 const int delay = 1000;// run every second ...
 const int executionCount = 10; // and 10 times
 
 int mInitialTimerValue = 10;

 PSyncTaskScheduler scheduler = new PSyncTaskScheduler();
 //count down 10 seconds
 SyncRepeatableTask repeatableTask = new SyncRepeatableTask(CountDown, OnEachCountDown, 
                delay, executionCount);
 scheduler.Enqueue(repeatableTask);

 // The MonoBehaviour update loop
 void Upate()
 {
    scheduler.Update()
 }
 
 object CountDown()
 {
    mInitialTimerValue--;
    return mInitialTimerValue;
 }
 
 void OnEachCountDown(object result)
 {   
   Debug.Log("The timer now is at "+result);   
   RenderTimer(mInitialTimerValue);
 }

Sunday 25 May 2014

Using PTaskScheduler - UniParallel Unity package


UniParallel is a .Net package designed to work with Unity and help developers to easily utilize the power of multi-core devices.

The PTaskScheduler class allows simple and easy scheduling of background tasks in a variety of ways.

There are 4 types of background tasks, each designed to behave in a very specific way


  • Use ImmediateTask if you want to schedule a task for immediate execution
  • Use TimedTask if you want to schedule a task for delayed execution
  • Use RepeatableTask to execute tasks a certain number of times on a interval
  • And use InfiniteTask  to execute tasks indefinitely on a certain interval

All scheduled tasks are executed without blocking the calling thread. While this can help increase parallelism in your app or game, it also means that you'll have to what for synchronization and concurrency problems.
When using this library in your Unity application, don't directly modify resources that are owner by the rendering thread.

You can and should, however, use this library for processing of any independent data processing, like loading of assets, processing object specific calculation, downloading files of the internet, so on and so forth.

PTaskScheduler is extremely light weight and designed to work without impacting the application using it.

Code examples


Scheduling tasks depends on 2 components: The task processor PTaskScheduler and a task. All tasks implement the IUniTask interface.

Let's start with the most simple task type: ImmediateTask

 [...]
 PTaskScheduler   taskScheduler = PTaskScheduler.Create();  
 IUniTask task = new ImmediateTask(ImmediateTaskProc);   
 taskScheduler.Enqueue(task);
 [...]

 private void ImmediateTaskProc()  
 {  
    System.Console.WriteLine("Hello from another thread");  
 }  


To schedule method to be executed after a certain delay, use  TimedTask
 [...]
 PTaskScheduler   taskScheduler = PTaskScheduler.Create();  
 int delayInMilliseconds=1000;
 IUniTask task = new TimedTask(DelayedTaskProc, delayInMilliseconds);   
 taskScheduler.Enqueue(task);
 [...]

 private void DelayedTaskProc()  
 {  
    System.Console.WriteLine("Hello after 1000 milliseconds");  
 }  


To schedule method to be executed repeatedly on interval, use  RepeatableTask
 [...]
 PTaskScheduler   taskScheduler = PTaskScheduler.Create();  
 int delayInMilliseconds=1000;
 int runCount = 10;
 IUniTask task = new RepeatableTask(RepeatableTaskProc, delayInMilliseconds, runCount);   
 taskScheduler.Enqueue(task);
 [...]

 private void RepeatableTaskProc()  
 {  
    System.Console.WriteLine("This will be executed 10 times every 1000 milliseconds");  
 }  


And if you want to schedule method to be executed indefinitely on interval, use  InfiniteTask. You can cancel the execution of any task by calling the method Cancel() on the task object in question
 [...]
 PTaskScheduler   taskScheduler = PTaskScheduler.Create();  
 int delayInMilliseconds=1000;
 IUniTask task = new InfiniteTask(InfiniteTaskProc, delayInMilliseconds);   
 taskScheduler.Enqueue(task);

 [...]
 // OK, time to shutdown
 task.Cancel(); 
 [...]

 private void InfiniteTaskProc()  
 {  
    System.Console.WriteLine("This will be executed indefinitely every 1000 milliseconds");  
 }  

Naturally, you can combine all the different task types described about and enqueue them in the same scheduler.

You can also schedule execute methods that have arguments using Lambda Expressions.
The example below uses a ImmediateTask but the same strategy applies to any task type.
 [...]
 PTaskScheduler   taskScheduler = PTaskScheduler.Create();  
 int delayInMilliseconds=1000;
 IUniTask task = new ImmediateTask(
            ()=>WithArgs(" text argument ", 42)
                        );
 taskScheduler.Enqueue(task);
 [...]
 private void WithArgs(string strArg, int intArg)
 {
   Debug.Log("string arg = "+strArg+" int arg "+intArg);
 }


This library is being actively used in many platforms that support .Net or Mono, including all supported by Unity.

If you have any questions or suggestions, please send a message to the gmail account junity3.