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.
我已经有一段时间没有使用。net桌面编程了,但是我喝了这个节点。js koolaid。节点有一些部分。我觉得和他一起工作很容易。特别是,我喜欢线程模型的简单性,并且我可以获得多线程应用程序的一些好处,同时只编写代码来跟踪单个线程。
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:
现在,我需要在。net中编写一个多线程应用程序,我突然想到,我没有理由不能使用用于构建Node的类似线程模型。js应用程序。我尤其想:
- Call long-running functions with callback parameters. (That function would execute on a thread from a pool. Maybe a simple wrapper function to call functions on new threads would be sufficient?)
- 使用回调参数调用长时间运行的函数。(该函数将在线程池中执行。也许在新线程上调用函数的简单包装函数就足够了?)
- Have those callback function calls ran on the "main" thread for processing
- 这些回调函数调用是否在“主”线程上运行以进行处理
- Maintain automatic synchronization for all objects accessed by this "main" thread, so locking isn't an issue
- 为这个“主”线程访问的所有对象维护自动同步,因此锁定不是问题
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?
这种线程模型的框架是否已经存在于。net应用程序中?如果没有,.NET中是否已经支持或处理了我正在寻找的一些功能?
4 个解决方案
#1
2
I would recommend the TPL. Here’s an example of how it works
我推荐TPL。这是它如何工作的一个例子
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
使用不同的调度器进行取消、错误处理等操作的可能性有很多。net 4.5允许使用wait
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.
在这里,编译器查看标记为异步的方法,并在保持代码可读的同时添加一堆线程安全健壮的任务同步代码。
#2
3
As others have mentioned, async
/ await
is an excellent choice for .NET. In particular:
正如其他人提到的,异步/等待是. net的一个很好的选择。特别是:
-
Task
/Task<T>
/TaskCompletionSource<T>
are analogous to JavaScript'sDeferred
/Promise
/Future
. -
任务/任务
/ TaskCompletionSource 类似于JavaScript的Deferred / Promise / Future。 - It's pretty easy to create JavaScript-style continuations using .NET-style continuations, but for the most part you won't need them.
- 使用. net样式的延续创建javascript样式的延续非常容易,但是在大多数情况下您不需要它们。
- There is no JavaScript equivalent to
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 anawait
. So you don't have to use continuation passing style. - 不存在与异步/等待等价的JavaScript。异步允许您像编写同步方法一样编写方法,并且在后台它将它们分解为连续,只要有等待。所以你不需要使用延续传递样式。
- For operations on a background thread, your best choice is
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. - 对于后台线程上的操作,最好的选择是Task.Run。然而,. net的标准模式是使用后台操作计算并返回一个值,而不是使用主线程进行连续的双向消息传递。
- If you do need a "stream" of asynchronous data, you should use TPL Dataflow or Rx. This is where things diverge from JS quite a bit.
- 如果您确实需要异步数据的“流”,您应该使用TPL Dataflow或Rx。这就是问题与JS有很大差异的地方。
I recommend you start with my async
/ await
intro post.
我建议您从我的async / waiting intro文章开始。
#3
1
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.
我建议您看看在。net 4.0中提供的TPL(任务并行库)。它可以做点1和2但不是3。
See http://msdn.microsoft.com/en-us/library/dd460717.aspx
参见http://msdn.microsoft.com/en-us/library/dd460717.aspx
#4
1
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.
下面的代码是一个POC,它处理你提到的所有3个点。但请注意,这只是一个POC。它不是类型安全的,它使用委托。动态调用可能很慢,但它证明了这个概念。
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:
输出:
#1
2
I would recommend the TPL. Here’s an example of how it works
我推荐TPL。这是它如何工作的一个例子
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
使用不同的调度器进行取消、错误处理等操作的可能性有很多。net 4.5允许使用wait
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.
在这里,编译器查看标记为异步的方法,并在保持代码可读的同时添加一堆线程安全健壮的任务同步代码。
#2
3
As others have mentioned, async
/ await
is an excellent choice for .NET. In particular:
正如其他人提到的,异步/等待是. net的一个很好的选择。特别是:
-
Task
/Task<T>
/TaskCompletionSource<T>
are analogous to JavaScript'sDeferred
/Promise
/Future
. -
任务/任务
/ TaskCompletionSource 类似于JavaScript的Deferred / Promise / Future。 - It's pretty easy to create JavaScript-style continuations using .NET-style continuations, but for the most part you won't need them.
- 使用. net样式的延续创建javascript样式的延续非常容易,但是在大多数情况下您不需要它们。
- There is no JavaScript equivalent to
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 anawait
. So you don't have to use continuation passing style. - 不存在与异步/等待等价的JavaScript。异步允许您像编写同步方法一样编写方法,并且在后台它将它们分解为连续,只要有等待。所以你不需要使用延续传递样式。
- For operations on a background thread, your best choice is
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. - 对于后台线程上的操作,最好的选择是Task.Run。然而,. net的标准模式是使用后台操作计算并返回一个值,而不是使用主线程进行连续的双向消息传递。
- If you do need a "stream" of asynchronous data, you should use TPL Dataflow or Rx. This is where things diverge from JS quite a bit.
- 如果您确实需要异步数据的“流”,您应该使用TPL Dataflow或Rx。这就是问题与JS有很大差异的地方。
I recommend you start with my async
/ await
intro post.
我建议您从我的async / waiting intro文章开始。
#3
1
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.
我建议您看看在。net 4.0中提供的TPL(任务并行库)。它可以做点1和2但不是3。
See http://msdn.microsoft.com/en-us/library/dd460717.aspx
参见http://msdn.microsoft.com/en-us/library/dd460717.aspx
#4
1
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.
下面的代码是一个POC,它处理你提到的所有3个点。但请注意,这只是一个POC。它不是类型安全的,它使用委托。动态调用可能很慢,但它证明了这个概念。
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:
输出: