.net 限制竞争执行的任务(引擎盖下)

.net 限制竞争执行的任务(引擎盖下),.net,async-await,.net,Async Await,考虑一个Microsoft.NET Framework应用程序,该应用程序充分利用async/await功能来生成许多同时(或嵌套)的任务。没有明显的“流程”控制,即任务大部分是独立的,不等待彼此完成,因此所有任务都在竞争执行 决定如何/何时调度或执行任务以及在哪个线程上执行任务的框架组件是什么 有哪些可能的过程会从用户代码影响该机制? 编辑 我有一个很好的句子,指出了我的问题的目的: 每当代码等待一个等待者说它尚未完成的等待者时(即,等待者的IsCompleted返回false),该方法需要

考虑一个Microsoft.NET Framework应用程序,该应用程序充分利用async/await功能来生成许多同时(或嵌套)的任务。没有明显的“流程”控制,即任务大部分是独立的,不等待彼此完成,因此所有任务都在竞争执行

决定如何/何时调度或执行任务以及在哪个线程上执行任务的框架组件是什么

有哪些可能的过程会从用户代码影响该机制?

编辑
我有一个很好的句子,指出了我的问题的目的:

每当代码等待一个等待者说它尚未完成的等待者时(即,等待者的IsCompleted返回false),该方法需要挂起

这里描述的时间点比我的问题晚了一步——有些东西已经处理了用户代码,构建了waitiable和waitier,很可能捕获了上下文,并决定了哪个线程将执行(或正在执行)任务。我是在问那个“事”

我确实看到了用户代码如何影响框架所采用的路径,但这些都是程序员的外部“教育”决定。假设我们采取每一种可能的途径,我们可以影响和过度饱和它。。。我们正在重击一些设施,对吗?也许没有一句话的答案。。。如果我在下面@Stephen的回答中看不到这一点,我很抱歉——我感谢你的帮助并继续挖掘

(一些相关的主题似乎在sync/await摘要下更深入,比如线程池、上下文。我要朝这个方向挖掘吗?)
结束编辑

自定义任务调度器是(最佳)(唯一)答案吗

出于这个问题的目的,我不考虑节流的任何外部因素,如资源不足(网络、I/O饱和)或业务原因(人为限制)。或者通过尝试跟踪正在执行的内容等方式来破解用户代码限制

同时(也与父子相关)任务

当一个
async
方法调用另一个方法时,可以将这些任务视为“父/子”关系。然而,从技术上讲,这并不是一个问题

控制调度、线程和执行的框架组件是什么

中以及中介绍了这一点。当
异步
方法通过
等待
挂起自身时,默认情况下,它将捕获当前的
同步上下文
(或者当前的
任务调度程序
,如果没有当前的
同步上下文
),并使用它来恢复该方法。这是默认行为;任何
async
方法都可以通过
wait
ing
configurewait(false)
的结果来选择不在其捕获的上下文上恢复。在绝大多数情况下,这将在线程池上执行方法的延续

你首先要考虑的是在任何地方都可以应用<代码> ConfigureAwait(false)<代码>。如果您始终如一地这样做,那么您的延续在很大程度上是由线程池管理的,线程池可以优化可用资源的使用。手动节流应该没有必要

也就是说,有几种方法可以做到这一点

TaskScheduler

您可以使用
TaskScheduler
来控制
async
延续的执行(只要它们不使用
ConfigureAwait(false)
)。在这个调度级别。然而,正如@svick所指出的,任务调度器只“看到”每个
async
方法的单个(同步)部分。因此,这通常不是人们寻求的解决方案

第三方物流数据流

如果您主要感兴趣的是限制粗粒度操作(可以根据需要有多个“子”操作),您可以使用
ActionBlock
从以下位置执行此操作:

var block=newactionblock(f=>f(),
新的ExecutionDataflowBlockOptions{MaxDegreeOfParallelism=…;});
block.Post(MyMethodAsync);
...

不同于<代码> TaskStalper-方法,TPL数据流的Max ReaveOfRealist考虑了 Aythc><代码>方法,包括所有的“子”任务和连续性。

一旦开始使用TPL数据流,您可能会发现应用程序逻辑的其他部分也会更自然地表达出来

异步信号量


如果您的节流需求更复杂,另一种方法是。然后,需要限制的每个
async
方法都将以
wait WaitAsync
开始执行,并以
Release
完成。通过将这些调用放置在您选择的位置,您可以完全控制限制的范围。

taskscheduler
s对于限制
async
s几乎毫无用处(除非您只想限制非异步部分)。你能解释一下你想做什么以及为什么你想限制
任务
s吗?@svick,我把用户代码看作是产生任务的泵(请求运行它们)。框架中的某些东西正在做出决定:“我将在同一个线程上运行此任务,因为…”,或者“我将生成一个线程,因为…”。如果你看到我思维中的缺陷——这正是我试图克服的——我正在努力学习——少想象,多了解。关于你的编辑:每个
async
方法开始同步执行;它只有在第一次点击它的
wait
(其中
!IsCompleted
)时才会变为异步。因此,要安排方法的第一部分,只需将
TaskFactory
与自定义
TaskScheduler
一起使用。听起来您真正想要的是一些介绍性的
async
材料。我建议你(按顺序)阅读:,the,the,
var block = new ActionBlock<Func<Task>>(f => f(),
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = ...; });
block.Post(MyMethodAsync);
...