net线程喜欢node . js / V8吗?

时间:2022-12-28 15:55:04

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's Deferred / 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 an await. 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:

输出:

net线程喜欢node . js / V8吗?

#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's Deferred / 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 an await. 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:

输出:

net线程喜欢node . js / V8吗?