我是否需要使用默认的TaskScheduler同步任务之间的资源访问? [英] Do I need to synchronize resource access between Tasks with the default TaskScheduler?

查看:47
本文介绍了我是否需要使用默认的TaskScheduler同步任务之间的资源访问?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Tasks和await/async进行编程.我假设多线程的工作方式与在NodeJS或Python中的工作方式相同,也就是说,并非如此,所有内容都在同一线程上运行.但是我一直在尝试学习如何实际执行任务,我的理解是它们是由TaskScheduler执行的.默认情况下,谁的实现是隐藏的,但可以预期使用ThreadPool.

I'm programming with Tasks and await/async. I assumed that the multithreading works like it does in NodeJS or Python, that is, it doesn't, everything just runs on the same thread. But I've been trying to learn how Tasks actually get executed and my understanding is that they're executed by TaskScheduler.Default who's implementation is hidden but can be expected to use a ThreadPool.

我应该像所有任务都可以在任何线程中运行一样进行编程吗?

Should I be programming as if all my Tasks can run in any thread?

我的异步编程的范围是相当轻量级的CPU工作,由几个无限循环组成,这些无限循环开始工作,然后在Task.Delay上等待几秒钟.现在,唯一的共享资源是一个整数,每次写入网络消息时该整数都会增加,但将来我希望我的任务将是共享字典和列表.

The extent of my asynchronous programming is fairly lightweight CPU work consisting of several infinite loops that do work and then await on Task.Delay for several seconds. Right now the only shared resources is an int that increments every time I write a network message but in the future I expect my tasks will be sharing Dictionaries and Lists.

我还有一个网络任务,该任务连接到TCP服务器并使用在BeginRead + EndRead上实现的任务读取消息.Read函数由一个无限循环调用,该循环读取一条消息,对其进行处理,然后再读取一条新消息.

I also have a network Task that connects to a TCP server and reads messages using a Task I implemented on BeginRead+EndRead. The Read function is called by an infinite loop that reads a messages, processes it, then reads a new message.

        void OnRead(IAsyncResult result)
        {
            var pair = (Tuple<TaskCompletionSource<int>, NetworkStream>)result.AsyncState;
            int count = pair.Item2.EndRead(result);
            pair.Item1.SetResult(count);
        }

        async Task<byte[]> Read(NetworkStream stream, uint size)
        {
            var result = new byte[size];
            var count = 0;
            while(count < size)
            {
                var tcs = new TaskCompletionSource<int>();
                stream.BeginRead(result, count, result.Length - (int)count, new AsyncCallback(OnRead), Tuple.Create(tcs, stream));
                count += await tcs.Task;
            }
            return result;
        }

我使用同步写入向NetworkStream写入.

I write to the NetworkStream using synchronous writes.

推荐答案

我假设多线程的工作方式与在NodeJS或Python中的工作方式相同,也就是说,并非如此,所有内容都在同一线程上运行.但是我一直在尝试学习如何实际执行任务,我的理解是它们是由TaskScheduler执行的.默认情况下,谁的实现是隐藏的,但可以预期使用ThreadPool.

I assumed that the multithreading works like it does in NodeJS or Python, that is, it doesn't, everything just runs on the same thread. But I've been trying to learn how Tasks actually get executed and my understanding is that they're executed by TaskScheduler.Default who's implementation is hidden but can be expected to use a ThreadPool.

不完全是.

首先,.NET中的 Task 可以为此处是不同类型任务的不同状态图.

First, Task in .NET can be two completely different things. Delegate Tasks represent code that can run on some thread, using a TaskScheduler to determine where and how they run. Delegate Tasks were introduced with the original Task Parallel Library and are almost never used with asynchronous code. The other kind of Task is Promise Tasks. These are much more similar to Promise in JavaScript: they can represent anything - they're just an object that is either "not finished yet" or "finished with a result" or "finished with an error". Here's a contrast of the different state diagrams for the different kinds of tasks.

因此,首先要认识到的是,就像您没有在JavaScript中执行承诺"一样,您也没有在.NET中执行(承诺)任务".因此,询问它在哪个线程上运行是没有意义的,因为它们不会在任何地方 run 运行.

So, the first thing to recognize is that just like you don't "execute a Promise" in JavaScript, you don't "execute a (Promise) Task" in .NET. So asking what thread it runs on doesn't make sense, since they don't run anywhere.

但是,JS和C#都具有 async / await 语言构造,允许您编写更自然的代码来实现 control 承诺.当 async 方法完成时,promise也就完成了.如果 async 方法抛出异常,则诺言就将失败.

However, both JS and C# have an async/await language construct that allows you to write more natural code to control promises. When the async method completes, the promise is completed; if the async method throws, the promise is faulted.

那么问题就变成了:控制此承诺的代码在哪里运行?

So the question then becomes: where does the code run that controls this promise?

在JavaScript世界中,答案是显而易见的:只有一个线程,因此就是代码运行的地方.在.NET世界中,答案要复杂一些.我的异步介绍给出了核心概念:每个就像其他任何方法一样,async 方法开始在调用线程上同步执行.当由于 await 而屈服时,它将捕获其上下文".然后,当该 async 方法准备在 await 之后恢复时,它将在该上下文"中恢复.

In the JavaScript world, the answer is obvious: there is only one thread, so that is where the code runs. In the .NET world, the answer is a bit more complex. My async intro gives the core concepts: every async method begins executing synchronously, on the calling thread, just like any other method. When it yields due to an await, it will capture its "context". Then, when that async method is ready to resume after the await, it resumes within that "context".

上下文"是 SynchronizationContext.Current ,除非它是 null ,在这种情况下,上下文是 TaskScheduler.Current .在现代代码中,上下文"通常是GUI线程上下文(始终在GUI线程上恢复)或线程池上下文(在任何可用的线程池线程上恢复).

The "context" is SynchronizationContext.Current, unless it is null, in which case the context is TaskScheduler.Current. In modern code, the "context" is usually either a GUI thread context (which always resumes on the GUI thread), or the thread pool context (which resumes on any available thread pool thread).

我应该像所有任务都可以在任何线程中运行一样进行编程吗?

Should I be programming as if all my Tasks can run in any thread?

async 方法中的代码可以在没有上下文的情况下在线程池线程上恢复.

The code in your async methods can resume on a thread pool thread if it's called without a context.

我是否需要在任务之间同步资源访问

Do I need to synchronize resource access between Tasks

可能不是. async await 关键字旨在简化串行代码的编写.因此,无需将 await 之前的代码与 await 之后的代码进行同步; await 之后的代码将总是 await 之前的代码之后运行,即使它在另一个线程上运行也是如此.另外, await 会注入所有必要的线程屏障,因此在乱序读取或类似操作方面不会有问题.

Probably not. The async and await keywords are designed to allow easy writing of serial code. So there's no need to synchronize code before an await with code after an await; the code after the await will always run after the code before the await, even if it runs on a different thread. Also, await injects all necessary thread barriers, so there's no issues around out-of-order reads or anything like that.

但是,如果您的代码同时运行多个 async 方法,并且这些方法共享数据,则需要对其进行同步.我有一篇博客文章介绍了这种偶然的隐式并行性(帖子末尾).一般来说,异步代码鼓励返回结果,而不是施加副作用.只要您这样做,隐式并行就不再是问题.

However, if your code runs multiple async methods at the same time, and those methods share data, then that would need to be synchronized. I have a blog post that covers this kind of accidental implicit parallelism (at the end of the post). Generally speaking, asynchronous code encourages returning results rather than applying side effects, and as long as you do that, implicit parallelism is less of a problem.

这篇关于我是否需要使用默认的TaskScheduler同步任务之间的资源访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆