【问题标题】:Inform that a long running async task is in progress - the right way通知一个长时间运行的异步任务正在进行中 - 正确的方式
【发布时间】:2019-04-08 20:30:43
【问题描述】:

我有一个控制台程序,它将异步 HTTP 请求发送到外部 Web API。 (HttpClient.GetAsync());)
这些任务可能需要几分钟才能完成 - 在此期间我希望能够向用户显示应用仍在运行 - 例如每 10 秒发送一次Console.WriteLine("I ain't dead - yet")。

我不知道如何正确地做,没有隐藏异常、引入死锁等的风险。

我知道 IProgress,但是我不知道我是否可以在这种情况下介绍它。我正在等待一个不报告进度的异步调用。 (本质上是一个调用httpClient GetAsync()方法的SDK

还有: 我无法将 GUI 设置为“InProgress”,因为没有 GUI,它是一个控制台应用程序 - 如果我不时不时发送更新消息,用户似乎会停止工作。

目前的想法:

            try
            {
                var task = httpClient.GetAsync(uri); //actually this is an SDK method call (which I cannot control and which does not report progress itself)

                while (!task.IsCompleted)
                {
                    await Task.Delay(1000 * 10);
                    this.Logger.Log(Verbosity.Verbose, "Waiting for reply...");
                }
                onSuccessCallback(task.Result);
            }
            catch (Exception ex)
            {
                if (onErrorCallback == null)
                {
                    throw this.Logger.Error(this.GetProperException(ex, caller));
                }
                this.Logger.Log(Verbosity.Error, $"An error when executing command [{action?.Command}] on {typeof(T).Name}", ex);
                onErrorCallback(this.GetProperException(ex, caller));
            }

【问题讨论】:

  • 你不能await说await action.Action()
  • @Rahul - 当然,但这样我会等待 5 分钟,而没有用户信息...
  • 实施 IProgress 怎么样? blogs.msdn.microsoft.com/dotnet/2012/06/06/…
  • @Fildor 实际上我会做相反的事情,我会将Timer 重构为上面的代码。原因是上面的代码可以在 WinForms、WPF 和控制台中正常工作,因为上面的代码使用了SynchronizationContext.Current。

标签: c# asynchronous async-await progress


【解决方案1】:

让我帮你整理一下这段代码

async Task Main()
{
    var reporter = new ConsoleProgress();
    var result = await WeatherWaxProgressWrapper(() => GetAsync("foo"), reporter);

    Console.WriteLine(result);
}



public async Task<int> GetAsync(string uri)
{
    await Task.Delay(TimeSpan.FromSeconds(10));
    return 1;
}

public async Task<T> WeatherWaxProgressWrapper<T>(Func<Task<T>> method, System.IProgress<string> progress)
{
    var task = method();
    while(!task.IsCompleted && !task.IsCanceled && !task.IsFaulted)
    {
        await Task.WhenAny(task, Task.Delay(1000));
        progress.Report("I ain't dead");
    }
    return await task;
}

public class ConsoleProgress : System.IProgress<string>
{
    public void Report(string value)
    {
        Console.WriteLine(value);
    }
}

【讨论】:

    【解决方案2】:

    您正在寻找System.Progress&lt;T&gt;,这是IProgress 的精彩实现。

    https://docs.microsoft.com/en-us/dotnet/api/system.progress-1

    您在“UI 线程”或您的情况下的主线程上创建此类的一个对象,它会为您捕获SynchronizationContext。将其传递给您的工作线程,每次对Report 的调用都将在捕获的线程上执行,您无需担心任何事情。

    在 WPF 或 WinForms 应用程序中非常有用。

    【讨论】:

      【解决方案3】:

      您可以拥有一个永无止境的Task 作为每 10 秒发出一次信号的信标,并在完成长时间运行的 I/O 操作后取消它:

      var beaconCts = new CancellationTokenSource();
      var beaconTask = Task.Run(async () =>
      {
          while (true)
          {
              await Task.Delay(TimeSpan.FromSeconds(10), beaconCts.Token);
              Console.WriteLine("Still going...");
          }
      });
      await LongRunningOperationAsync();
      beaconCts.Cancel();
      

      【讨论】:

        猜你喜欢
        • 2014-12-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-03
        • 1970-01-01
        • 2016-09-28
        • 2013-12-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多