如何使用async-await创建一个永远不会一次执行多个任务的调度程序?

Nil*_*zor 18 async-await c#-4.0

我想实现一个类或模式,以确保我从不为一组操作(HTTP调用)一次执行多个任务.任务的调用可以随机地来自不同的线程.我想使用async-await模式,以便调用者可以通过在try-catch中包装调用来处理异常.

以下是预期执行流程的说明:

在此输入图像描述

来自调用者的伪代码:

try {
    Task someTask = GetTask();
    await SomeScheduler.ThrottledRun(someTask);
} 
catch(Exception ex) { 
    // Handle exception
}
Run Code Online (Sandbox Code Playgroud)

Task这里的类可能是一个Action类,取决于解决方案.

请注意,当我在此问题中使用"Schedule"一词时,我不一定将其与.NET Task Scheduler相关联使用.我不知道async-await库是否足以知道在什么角度以及使用什么工具来解决这个问题.TaskScheduler可能与此相关,也可能不相关.我已经阅读了TAP模式文档并找到了几乎可以解决这个问题的模式,但并不完全(关于交错的章节).

Ste*_*ary 29

ConcurrentExclusiveSchedulerPair.NET 4.5中有一种新类型(我不记得它是否包含在Async CTP中),您可以使用它将ExclusiveScheduler执行限制Task为一次.

考虑将您的问题构建为数据流.只需将一个TaskScheduler块选项传递给您想要限制的数据流部分即可.

如果您不想(或不能)使用Dataflow,您可以自己做类似的事情.请记住,在TAP中,您总是返回已启动的任务,因此您没有像在TPL中那样将"创建"与"调度"分开.

您可以使用这样ConcurrentExclusiveSchedulerPair来安排Actions(或async没有返回值的lambda):

public static ConcurrentExclusiveSchedulerPair schedulerPair =
    new ConcurrentExclusiveSchedulerPair();
public static TaskFactory exclusiveTaskFactory =
    new TaskFactory(schedulerPair.ExclusiveScheduler);
...
public static Task RunExclusively(Action action)
{
  return exclusiveTaskFactory.StartNew(action);
}
public static Task RunExclusively(Func<Task> action)
{
  return exclusiveTaskFactory.StartNew(action).Unwrap();
}
Run Code Online (Sandbox Code Playgroud)

关于这一点有几点需要注意:

  • ConcurrentExclusiveSchedulerPairTask排队到其调度程序的坐标s的单个实例.第二个实例ConcurrentExclusiveSchedulerPair将独立于第一个实例,因此您必须确保在您想要协调的系统的所有部分中使用相同的实例.
  • async默认情况下,方法将在TaskScheduler启动它的方法上恢复.所以这意味着如果一个async方法调用另一个async方法,"child"方法将"继承"父方法TaskScheduler.任何async方法可以选择不继续就其TaskScheduler使用ConfigureAwait(false)(在这种情况下,直接继续线程池).

  • `Func <Task>`不能那样工作,因为`ConcurrentExclusiveSchedulerPair`允许一次执行一个任务,所以当`async`方法`等待时,它就不再执行了.如果你需要阻塞直到整个*`async`方法完成,你可能最好使用[`async` lock](http://blogs.msdn.com/b/pfxteam/archive/2012/ 2月12日/ 10266988.aspx). (6认同)
  • 如果使用带有`async`动作的`ExclusiveScheduler`,请注意其中几个动作的执行可以交错.因此,当第一个动作产生时,第二个动作可能开始执行.但是第一个动作的其余部分在第二个动作产生或结束之前不会开始.如果这不是您想要的,您可以使用带有"MaxDegreeOfParallelism = 1"的TPL数据流块. (5认同)