像Node.js / V8一样的.NET线程?

我已经离开了.NET桌面编程一段时间了,同时喝了Node.js koolaid。 Node.js的某些部分我觉得很容易使用。 特别是,我喜欢线程模型的简单性,并且我可以获得multithreading应用程序的一些好处,同时只编写代码来跟踪单个线程。

现在,我需要在.NET中编写一个multithreading应用程序,我想到我没有理由不使用用于构建Node.js应用程序的类似线程模型 。 特别是,我想:

  • 使用回调参数调用长时间运行的函数。 (该函数将在池中的线​​程上执行。也许在新线程上调用函数的简单包装函数就足够了吗?)
  • 让那些回调函数调用在“主”线程上运行以进行处理
  • 保持此“主”线程访问的所有对象的自动同步,因此锁定不是问题

这个线程模型的这种框架是否已存在于.NET应用程序中? 如果没有,是否有部分.NET已经支持或处理我正在寻求的一些function?

我会推荐TPL。 这是一个如何工作的例子

Void Work() { Task ts = Get(); ts.ContinueWith(t => { string result = t.Result; Console.WriteLine(result); }); } 

取消,使用不同的调度程序等进行error handling有各种各样的可能性。使用.Net 4.5,您可以使用等待

 async void Work() { Task ts = Get(); string result = await ts; Console.WriteLine(result); } 

这里编译器查看标记为async的方法,并添加一大堆线程安全健壮的任务同步代码,同时保持代码可读。

正如其他人所提到的, async / await是.NET的绝佳选择。 特别是:

  • Task / Task / TaskCompletionSource 类似于 JavaScript的Deferred / Promise / Future
  • 使用.NET风格的延续创建JavaScript风格的延续很容易 ,但在大多数情况下你不需要它们。
  • 没有相当于async / await JavaScript。 async允许您编写方法,就好像它们是同步的一样,并且它会在需要await地方将它们分解为延续。 所以你不必使用延续传递方式。
  • 对于后台线程上的操作,最好的选择是Task.Run 。 但是,.NET的标准模式是让后台操作计算并返回单个值,而不是与主线程进行连续的双向消息传递。
  • 如果确实需要异步数据的“流”,则应使用TPL Dataflow或Rx 。 这是事情与JS分歧的地方。

我建议你从我的async / await简介开始。

我建议看一下在.Net 4.0中可用的TPL(任务并行库)。 它可以做点1和2但不能做3点。

请参阅http://msdn.microsoft.com/en-us/library/dd460717.aspx

除了其他选项之外,还可以通过利用Window的本机事件循环来实现。

以下代码是相同的POC,它解决了您提到的所有3个点。 但请注意,它只是一个POC。 它不是类型安全的,它使用Delegate.DynamicInvoke,它可能很慢,但它certificate了这个概念。

 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 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); } } 

客户代码:

  [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(LongCalculation), new object[]{5,6}, new Action(PrintResult)); Console.WriteLine("Queued Long Running Task"); Thread.Sleep(400); //Do more work EventLoop.EnqueueAsyncTask(new Func(LongCalculation), new object[] { 15, 16 }, new Action(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 } 

输出:

在此处输入图像描述