ChatGPT解决这个技术问题 Extra ChatGPT

将基于回调的异步方法转换为等待任务的最佳方法

将使用回调的“经典”异步方法转换/包装为返回(等待)任务的东西的最佳方法是什么?

例如,给定以下方法:

public void GetStringFromUrl(string url, Action<string> onCompleted);

我知道将其包装到返回任务的方法中的唯一方法是:

public Task<string> GetStringFromUrl(string url)
{
     var t = new TaskCompletionSource<string>();

     GetStringFromUrl(url, s => t.TrySetResult(s));

     return t.Task;
}

这是实现这一目标的唯一方法吗?

有没有办法在任务本身中包装对 GetStringFromUrl(url,callback) 的调用(即调用本身将在任务内部运行而不是同步运行)

顺便说一句,这不是“经典”的 .net 异步方法。这些是 BeginXxx()EndXxx() 对。另外,您为什么要寻找其他方法来做到这一点?你希望得到什么?
我只是想确保我不会错过一些明显的替代方法来做同样的事情。
有什么理由在这里使用 TrySetResult 而不是 SetResult 吗?

s
svick

您的代码简短,可读且高效,因此我不明白您为什么要寻找替代方案,但我想不出任何东西。我认为你的方法是合理的。

我也不确定为什么您认为原始版本中的同步部分没问题,但您想在基于 Task 的版本中避免它。如果您认为同步部分可能需要太长时间,请为该方法的两个版本修复它。

但如果您只想在 Task 版本中异步运行它(即在 ThreadPool 上),您可以使用 Task.Run()

public Task<string> GetStringFromUrl(string url)
{
    return Task.Run(() =>
    {
        var t = new TaskCompletionSource<string>();

        GetStringFromUrl(url, s => t.TrySetResult(s));

        return t.Task;
    });
}

我并不总是控制现有的基于回调的方法,所以如果方法的同步部分正在做一些耗时太长的事情,我想也必须选择在任务中移动它。您的解决方案正好解决了这个问题。
嗯...我假设带有回调的原始 GetStringFromUrl 已经是异步的(因此回调)。如果是这样,这个建议将消耗资源,而只是为了调用调用而从另一个任务中分离出来。
@DrewMarsh 即使是异步方法通常也有一些同步部分,尽管大多数时候它可以忽略不计。但是这个问题专门提出了这个问题,否则我不会提出类似的建议。
@svick 当然,通常会有一些启动成本,但是除非有人创建了一个非常劣质的实现,否则我会花掉更多的开销,而不是仅仅让那部分在 Task.Run 上执行当前线程。我认为你必须相信你在这方面调用的异步 API,直到你能证明它有罪,我个人绝不建议默认这样做。只是我的2美分。
如果这个方法真的是异步的,那么启动一个新任务将失去异步运行作业的所有优点。如果您愿意,您可以在方法调用之前调用 Task.Yield() 将其发布到任务调度程序中。无论如何,问题的代码都会更好。
D
Drew Marsh

假设回调只处理成功的情况,您假设的实现对此非常好。如果在 GetStringFromUrl 实现的异步基础中发生异常,当前会发生什么?他们没有真正的方法将其传播到 Action 回调......他们是否只是吞下它并返回 null 或其他东西?

我唯一推荐的是遵循使用 XXXAsync 后缀命名此类异步方法的新约定。