为什么不等待我传递给Task.Run()的异步操作?

时间:2019-07-16 09:02:19

标签: c# .net asynchronous task

我在这里真的很奇怪,我缺少一些东西,但是现在已经为这个问题苦苦挣扎了几个小时,却无法解决。我有一种任务调度类,该类主要接收正常启动的同步操作,而不是通过Task.Run()异步启动。但是,当它获得传递的异步Action时,它将返回,而无需等待任务等待。我将其简化为以下示例:

private async void Button_OnClick(object sender, RoutedEventArgs e)
{
    Logger("Click event received");
    await OperationProcessor(async () =>
    {
        Logger(">>> Starting ACTION, waiting 5 seconds");
        await Task.Delay(5000);
        Logger(">>> ACTION finished");
    });
    Logger("Click event finished");
}

private static async Task OperationProcessor(Action operation)
{
    Logger("Processing started, waiting a second");
    await Task.Delay(1000);
    Logger("Launching action");
    await Task.Run(operation);
    Logger("Returned from action, waiting another second");
    await Task.Delay(1000);
    Logger("Processing finished");
}

private static void Logger(string message)
{
    Console.WriteLine($"{DateTime.Now:dd.MM.yy HH:mm:ss} [{Thread.CurrentThread.ManagedThreadId}] {message}");
}

这将产生以下输出:

1: 16.07.19 10:58:06 [1] Click event received
2: 16.07.19 10:58:06 [1] Processing started, waiting a second
3: 16.07.19 10:58:07 [1] Launching action
4: 16.07.19 10:58:07 [7] >>> Starting ACTION, waiting 5 seconds
5: 16.07.19 10:58:07 [1] Returned from action, waiting another second
6: 16.07.19 10:58:08 [1] Processing finished
7: 16.07.19 10:58:08 [1] Click event finished
8: 16.07.19 10:58:12 [7] >>> ACTION finished

如何使5等待开始于4的动作?

3 个答案:

答案 0 :(得分:1)

当您传递异步调用时,您不需要将其重新包装在线程中

您需要做的就是

private static async Task OperationProcessor(Action operation)
{
    Logger("Processing started, waiting a second");
    await Task.Delay(1000);
    Logger("Launching action");
    operation();
    Logger("Returned from action, waiting another second");
    await Task.Delay(1000);
    Logger("Processing finished");
}

尽管您发现异步操作的签名正在返回一个任务,所以对异步调用的操作将相同

private static async Task OperationProcessor(Func<Task> operation)
{
    Logger("Processing started, waiting a second");
    await Task.Delay(1000);
    Logger("Launching action");
    await operation();
    Logger("Returned from action, waiting another second");
    await Task.Delay(1000);
    Logger("Processing finished");
}

答案 1 :(得分:1)

  

为什么不等待传递给Task.Run()的异步动作?

因为不可能awaitvoid

谈论“等待方法”或“等待委托”是很常见的做法,但这可能会引起误解。实际发生的情况是该方法先被调用,然后等待 returns 。并且不可能等待Action的结果,因为该委托返回了void

这是avoid async void很好的主要原因。在这种情况下,async void有点偷偷摸摸,因为它是lambda表达式。

void Method()异步等效的是async Task MethodAsync(),因此asynchronous delegate equivalent of Action is Func<Task>。这就是为什么添加Func<Task>重载起作用的原因。您可以同时保留ActionFunc<Task>重载,并且编译器会明智地选择Func<Task> lambda作为async重载。

  

我有一种任务调度类

如果您的Func<Task>解决方案足够好,请保持现状。但是,如果您想更深入一点,可以检测(并等待)async void方法。 async void个方法interact with the current SynchronizationContext。由于您拥有自己的“计划课程”,因此您可能要考虑为该课程计划的代码提供SynchronizationContext。如果您有兴趣,请随时向我的AsyncContext class借用,它实际上只是带有SynchronizationContext的单线程工作队列。

答案 2 :(得分:0)

知道了...更改方法签名可以完成以下任务:

    private static async Task OperationProcessor(Func<Task> operation) {
         // Body unchanged
    }