【问题标题】:Async method with an async lambda expression带有异步 lambda 表达式的异步方法
【发布时间】:2018-09-21 12:47:14
【问题描述】:

我有一个内部带有异步调用的 lambda 表达式

public async Task UploadFile(string id)
{
    Progress<double> progress = new Progress<double>(async x =>
    {
        await Clients.Client(id).SendAsync("FTPUploadProgress", x);
    });
    await client.DownloadFileAsync(localPath, remotePath, true, FluentFTP.FtpVerify.Retry, progress);
}

我想在进度改变时调用 async 方法。

我收到以下警告:

此异步方法缺少“等待”运算符,将同步运行。 考虑使用 'await' 运算符来等待非阻塞 API 调用

我怎样才能使这个方法异步?

我应该用 System.Action&lt;T&gt; 类重写 lambda 吗?

【问题讨论】:

  • UploadFile 被标记为async 但其中没有一个await
  • 为什么在进度委托中有SendAsync 调用?你应该把它移到外部方法中。那么如果你想使用进度,你应该将它传递给一个接受Progress参数的方法,并在lambda表达式中实现进度逻辑。
  • 我本来希望将 IProgress 参数传递给 SendAsync ... 就像您将 CancellationToken 一样。
  • “我想在进度改变时调用该方法” 您应该将此添加到您的问题中。这是一个完全不同的游戏。
  • @myro 因为警告中没有await 运算符async 方法需要有一个 await 运算符,否则它不是异步的。您在进度委托中拥有的那个属于它。

标签: c# asp.net-core


【解决方案1】:

使用事件处理程序并在进度操作中引发它。事件处理程序将允许异步调用。

幸运的是,Progress&lt;T&gt; 有一个您可以订阅的 ProgressChanged 事件。

根据原始问题中提供的代码查看以下示例

public async Task UploadFile(string id) {
    EventHandler<double> handler = null;
    //creating the handler inline for compactness
    handler = async (sender, value) => {
        //send message to client asynchronously
        await Clients.Client(id).SendAsync("FTPUploadProgress", value);
    };
    var progress = new Progress<double>();
    progress.ProgressChanged += handler;//subscribe to ProgressChanged event

    //use the progress as you normally would
    await client.DownloadFileAsync(localPath, remotePath, true, FluentFTP.FtpVerify.Retry, progress);

    //unsubscribe when done 
    progress.ProgressChanged -= handler;
}

所以现在当报告进度时,事件处理程序可以进行异步调用。

参考Async/Await - Best Practices in Asynchronous Programming

另一种选择是创建您自己的 IProgress&lt;T&gt; 实现,该实现采用允许异步调用的 Func&lt;Task&lt;T&gt;&gt;,但这可能有点矫枉过正。

【讨论】:

  • 对我来说似乎有点令人费解。是否有理由不使用直接采取行动的 CTOR?
  • @Fildor 操作将是async void,通常不建议这样做。事件处理程序是关于async void 的唯一例外。检查答案中的链接文章。
  • 啊,现在我明白了。我完全错过了void
【解决方案2】:

我认为您误解了 Progress&lt;T&gt; 类的用法。编译器抱怨说,您的方法 UploadFile 缺少 await 运算符。当你的 lambda 被调用时,它会异步执行。

下面是一个简短的总结如何使用IProgressyT&gt;接口:

如果您有一个应该支持报告进度的方法,它可以将IProgress&lt;T&gt; 作为参数并通过此对象传达其端口。它执行受监控的操作。每次在 Progress&lt;T&gt; 上调用 Report() 方法时,都会执行您提供的 Lambda。此 Lambda 通常用于更新 UI。

这是一个例子。

public async Task DoStuff(IProgress<double> progress = null)
{
     for(int i = 0; i < 100; ++i)
     {
         await Task.Delay(500);
         progress?.Report((double)(i +1) / 100);
     }
}


// somewhere else in your code
public void StartProgress(){
    var progress = new Progress(p => Console.WriteLine($"Progress {p}"));
    DoStuff(progress);  
}

【讨论】:

  • 微软其实推荐progress?.Report((double)(i +1) / 100);(处理进度,让它可以为空)
  • @Fildor 我很惊讶我忘记了“?。”这里。我会改变答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多