【问题标题】:How to use await keyword inside a method without changing the method async如何在方法中使用 await 关键字而不更改方法异步
【发布时间】:2017-01-12 23:32:22
【问题描述】:

我正在开发一个使用 Quartz.net 将消息发送到消息队列的计划作业。 IJob 的 Execute 方法不是异步的。所以我不能使用异步任务。但我想用 await 关键字调用一个方法。

请在下面找到我的代码。不知道我做的是否正确。谁能帮我解决这个问题?

private async Task PublishToQueue(ChangeDetected changeDetected)
{
    _logProvider.Info("Publish to Queue started");

    try
    {
       await _busControl.Publish(changeDetected);

        _logProvider.Info($"ChangeDetected message published to RabbitMq. Message");
    }
    catch (Exception ex)
    {
        _logProvider.Error("Error publishing message to queue: ", ex);

        throw;
    }
}

public class ChangedNotificatonJob : IJob
{
    public void Execute(IJobExecutionContext context)
    {
                    //Publish message to queue
                    Policy
                        .Handle<Exception>()
                        .RetryAsync(3, (exception, count) =>
                        {
                            //Do something for each retry
                        })
                        .ExecuteAsync(async () =>
                        {
                            await PublishToQueue(message);
                        });
    }
}

这是正确的方法吗?我用过 .GetAwaiter();

Policy
        .Handle<Exception>()
        .RetryAsync(_configReader.RetryLimit, (exception, count) =>
        {
            //Do something for each retry
        })
        .ExecuteAsync(async () =>
        {
            await PublishToQueue(message);
        }).GetAwaiter()

【问题讨论】:

标签: c#-4.0 async-await quartz.net polly


【解决方案1】:

Polly 的 .ExecuteAsync() 返回一个 Task。对于任何Task,您只需在其上调用.Wait()(或其他阻塞方法)即可同步阻塞,直到它完成或抛出异常。

正如您所观察到的,由于IJob.Execute(...) 不是async,因此您不能使用await,因此您别无选择,只能在任务上同步阻塞,如果您想发现成功-或- 否则在IJob.Execute(...) 返回之前发布。

.Wait() 将导致任务中的任何异常被重新抛出,并包裹在AggregateException 中。如果所有 Polly 安排的重试都失败,则会发生这种情况。

您需要决定如何处理该异常:

  • 如果您希望调用者处理它,请重新抛出它或不捕获它,让它在 Quartz 作业之外级联。

  • 如果你想在从IJob.Execute(...) 返回之前处理它,你需要一个try {} catch {} 围绕整个.ExecuteAsync(...).Wait()。或者考虑 Polly 的 .ExecuteAndCaptureAsync(...) 语法:它避免了你必须提供外部 try-catch,而是将执行的最终结果放入 PolicyResult 实例中。请参阅Polly doco


如果您的唯一目的是在某处记录消息发布失败,并且您不关心该记录是否在IJob.Execute(...) 返回之前发生,则还有另一种选择。在这种情况下,您可以使用.ContinueWith(...) 将延续任务链接到ExecuteAsync(),而不是使用.Wait(),并在那里处理任何登录。我们采用这种方法,并将失败的消息发布到一个特殊的“消息医院”——捕获足够的信息,以便我们可以选择是否在适当的时候再次重新发布该消息。这种方法是否有价值取决于永不丢失信息对您的重要性。


编辑:GetAwaiter() 无关紧要。它不会神奇地让您在非async 方法中开始使用await

【讨论】:

  • 您可能还想看到这个:quartz-scheduler.net/2016/08/16/…。 Quartz.Net 计划在 v3 版本中添加async/await 支持(目前在 NuGet 上预览)。大概这应该给你一个IJobAsyncIAsyncJob 接口或类似的接口。在撰写本文时它不是documentated
  • GetAwaiter().GetResult()Wait() 的更好替代品,因为它避免了 AggregateException 包装器。
  • 我很想听听 Quartz.NET 3.0 alpha 2 在您的使用中的表现。它具有您正在寻找的异步/等待任务支持。 Migration guide 记录了大部分差异。
猜你喜欢
  • 2017-11-06
  • 1970-01-01
  • 2021-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多