Reputation: 163262
I've been away from .NET desktop programming for some time, while drinking the Node.js koolaid. There are some parts of Node.js I find easy to work with. In particular, I like the simplicity of the threading model, and that I can have a few of the benefits of a multithreaded application while only writing code to keep track of a single thread.
Now, I have a need to write a multi-threaded application in .NET, and it occurred to me that there is no reason I cannot use a similar threading model that is used to build Node.js applications. In particular, I want to:
Does such a framework for this threading model already exist within, or for .NET applications? If not, are there parts of .NET that already support or handle some of the functionality that I am seeking?
Upvotes: 5
Views: 909
Reputation: 2736
It can be achieved, among other options, by taking advantage of Window's native event loop.
Following code is a POC for the same and it addresses all the 3 points you have mentioned. But note that it is just a POC. It is not type safe and it uses Delegate.DynamicInvoke which can be slow but it proves the concept nevertheless.
public static class EventLoop
{
private class EventTask
{
public EventTask(Delegate taskHandler) : this(taskHandler, null) {}
public EventTask(Delegate taskHandler, Delegate callback)
{
TaskHandler = taskHandler;
Callback = callback;
}
private Delegate Callback {get; set;}
private Delegate TaskHandler {get; set;}
public void Invoke(object param)
{
object[] paramArr = null;
if (param.GetType().Equals(typeof(object[])))
{
paramArr = (object[]) param; //So that DynamicInvoke does not complain
}
object res = null;
if (TaskHandler != null)
{
if (paramArr != null)
{
res = TaskHandler.DynamicInvoke(paramArr);
}
else
{
res = TaskHandler.DynamicInvoke(param);
}
}
if (Callback != null)
{
EnqueueSyncTask(Callback, res);
}
}
}
private static WindowsFormsSynchronizationContext _syncContext;
public static void Run(Action<string[]> mainProc, string[] args)
{
//You need to reference System.Windows.Forms
_syncContext = new WindowsFormsSynchronizationContext();
EnqueueSyncTask(mainProc, args);
Application.Run();
}
public static void EnqueueSyncTask(Delegate taskHandler, object param)
{
//All these tasks will run one-by-one in order on Main thread
//either on call of Application.DoEvenets or when Main thread becomes idle
_syncContext.Post(new EventTask(taskHandler).Invoke, param);
}
public static void EnqueueAsyncTask(Delegate taskHandler, object param, Delegate callback)
{
//En-queue on .Net Thread Pool
ThreadPool.QueueUserWorkItem(new EventTask(taskHandler, callback).Invoke, param);
}
}
Client Code:
[STAThread]
static void Main(string[] args)
{
Thread.CurrentThread.Name = "MAIN THREAD";
Console.WriteLine("Method Main: " + Thread.CurrentThread.Name);
EventLoop.Run(MainProc, args);
}
static void MainProc(string[] args)
{
Console.WriteLine("Method MainProc: " + Thread.CurrentThread.Name);
Console.WriteLine("Queuing Long Running Task...");
EventLoop.EnqueueAsyncTask(new Func<int,int,int>(LongCalculation), new object[]{5,6}, new Action<int>(PrintResult));
Console.WriteLine("Queued Long Running Task");
Thread.Sleep(400); //Do more work
EventLoop.EnqueueAsyncTask(new Func<int, int, int>(LongCalculation), new object[] { 15, 16 }, new Action<int>(PrintResult));
Thread.Sleep(150); //Do some more work but within this time 2nd task is not able to complete, meanwhile 1st task completes
//Long running Tasks will run in background but callback will be executed only when Main thread becomes idle
//To execute the callbacks before that, call Application.DoEvents
Application.DoEvents(); //PrintResult for 1st task as 2nd is not yet complete
Console.WriteLine("Method MainProc: Working over-time!!!!");
Thread.Sleep(500); //After this sleep, 2nd Task's print will also be called as Main thread will become idle
}
static int LongCalculation(int a, int b)
{
Console.WriteLine("Method LongCalculation, Is Thread Pool Thread: " + Thread.CurrentThread.IsThreadPoolThread);
Console.WriteLine("Running Long Calculation");
Thread.Sleep(500); //long calc
Console.WriteLine("completed Long Calculation");
return a + b;
}
static void PrintResult(int a)
{
Console.WriteLine("Method PrintResult: " + Thread.CurrentThread.Name);
Console.WriteLine("Result: " + a);
//Continue processing potentially queuing more long running tasks
}
Output:
Upvotes: 1
Reputation: 456537
As others have mentioned, async
/ await
is an excellent choice for .NET. In particular:
Task
/ Task<T>
/ TaskCompletionSource<T>
are analogous to JavaScript's Deferred
/ Promise
/ Future
.async
/ await
. async
allows you to write your methods as though they were synchronous, and under the hood it breaks them up into continuations wherever there's an await
. So you don't have to use continuation passing style.Task.Run
. However, the standard pattern for .NET is to have the background operation compute and return a single value, instead of having continuous bidirectional messaging with the main thread.I recommend you start with my async
/ await
intro post.
Upvotes: 3
Reputation: 1724
I would recommend the TPL. Here’s an example of how it works
Void Work()
{
Task<string> ts = Get();
ts.ContinueWith(t =>
{
string result = t.Result;
Console.WriteLine(result);
});
}
There are a whole range of possibilities for cancelation, error handling using different schedulers etc. With .Net 4.5 you have the possibility of using await
async void Work()
{
Task<string> ts = Get();
string result = await ts;
Console.WriteLine(result);
}
Here the compiler looks at methods marked async and adds a whole pile of thread safe robust task synchronizing code while leaving the code readable.
Upvotes: 2
Reputation: 35477
I recommend a look at TPL (Task Parallel Library) which became available in .Net 4.0. It can do points 1 and 2 but not 3.
See http://msdn.microsoft.com/en-us/library/dd460717.aspx
Upvotes: 1