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

C# 为什么不等待我传递给Task.Run()的异步操作?,c#,.net,asynchronous,task,C#,.net,Asynchronous,Task,我在这里有一种非常奇怪的感觉,我错过了一些东西,但我为这个问题挣扎了几个小时,却无法得到它。我有一种任务调度类,它主要接收正常的同步操作,而不是通过task.Run()异步启动。但当异步操作被传递时,它返回,而不等待任务被等待。我已将其简化为以下示例: private async void Button_OnClick(object sender, RoutedEventArgs e) { Logger("Click event received"); await Operati

我在这里有一种非常奇怪的感觉,我错过了一些东西,但我为这个问题挣扎了几个小时,却无法得到它。我有一种任务调度类,它主要接收正常的同步操作,而不是通过task.Run()异步启动。但当异步操作被传递时,它返回,而不等待任务被等待。我已将其简化为以下示例:

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点开始的操作?

明白了。。。更改方法签名会执行以下操作:

    private static async Task OperationProcessor(Func<Task> operation) {
         // Body unchanged
    }
private静态异步任务操作处理器(Func操作){
//主体不变
}

明白了。。。更改方法签名会执行以下操作:

    private static async Task OperationProcessor(Func<Task> operation) {
         // Body unchanged
    }
private静态异步任务操作处理器(Func操作){
//主体不变
}

当您传入异步调用时,不需要在线程中重写它

对于一个动作,你需要做的就是

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");
}
private静态异步任务操作处理器(Func操作)
{
记录器(“处理已启动,等待一秒钟”);
等待任务。延迟(1000);
记录器(“发射行动”);
等待操作();
记录器(“从操作返回,等待另一秒”);
等待任务。延迟(1000);
记录器(“处理完成”);
}

当您传入异步调用时,不需要在线程中重写它

对于一个动作,你需要做的就是

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");
}
private静态异步任务操作处理器(Func操作)
{
记录器(“处理已启动,等待一秒钟”);
等待任务。延迟(1000);
记录器(“发射行动”);
等待操作();
记录器(“从操作返回,等待另一秒”);
等待任务。延迟(1000);
记录器(“处理完成”);
}
为什么不等待我传递给Task.Run()的异步操作

因为它不可能等待
一个
无效

谈论“等待一个方法”或“等待一个委托”是很常见的,但这可能会产生误导。实际上,首先调用该方法,然后等待它返回的任何内容。而且不可能等待
操作的结果,因为该委托返回
void

这就是为什么这样做很好的主要原因。在本例中,
async void
很狡猾,因为它是一个lambda表达式

void Method()
的异步等价物是
async Task MethodAsync()
,因此。这就是添加
Func
重载有效的原因。您可以同时保留
操作
Func
重载,编译器将智能地为
async
lambdas选择
Func
重载

我有一个任务调度类

如果您的
Func
解决方案足够好,请保持原样。但是,如果您想更深入一点,可以检测(并等待)
async void
方法<代码>异步无效
方法。因为您有自己的“调度类”,所以<<强>可能<>强>要考虑为该类安排的代码提供<代码>同步化上下文< /代码>。如果您感兴趣,请随意借用my,它本质上只是一个带有
SynchronizationContext
的单线程工作队列

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

因为它不可能等待
一个
无效

谈论“等待一个方法”或“等待一个委托”是很常见的,但这可能会产生误导。实际上,首先调用该方法,然后等待它返回的任何内容。而且不可能等待
操作的结果,因为该委托返回
void

这就是为什么这样做很好的主要原因。在本例中,
async void
很狡猾,因为它是一个lambda表达式

void Method()
的异步等价物是
async Task MethodAsync()
,因此。这就是添加
Func
重载有效的原因。您可以同时保留
操作
Func
重载,编译器将智能地为
async
lambdas选择
Func
重载

我有一个任务调度类


如果您的
Func
解决方案足够好,请保持原样。但是,如果您想更深入一点,可以检测(并等待)
async void
方法<代码>异步无效
方法。因为您有自己的“调度类”,所以<<强>可能<>强>要考虑为该类安排的代码提供<代码>同步化上下文< /代码>。如果你感兴趣,请随意借用my,它本质上只是一个单线程工作队列,带有
SynchronizationContext

任务。Run将您的操作包装在一个线程中,因此您正在等待触发任务的任务,而不是任务本身,因为您的操作已经是一个自包含的线程,您只需要调用该操作而不是包装它,Task.Run将您的操作包装在一个线程中,因此您正在等待触发任务的任务,而不是任务本身,因为您的操作已经是一个自包含的线程,您只需调用该操作,而不是包装它,谢谢。这使我理清了思路:)尽管我自己已经接近解决方案了。我迷失了方向,因为我的方法主要是为同步方法编写的,所以包装是必要的。我最终为异步操作创建了一个单独的重载。谢谢。@Programmierus通过声明OperationProce