首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当将旧的异步模式包装到TaskCompletionSource中时,还需要等待吗?

当将旧的异步模式包装到TaskCompletionSource中时,还需要等待吗?
EN

Stack Overflow用户
提问于 2019-02-18 10:48:16
回答 5查看 392关注 0票数 1

我正在学习C# Asnc-等待模式,目前正在阅读C#烹饪书中的并发性。

他讨论了用TaskCompletionSource (TCS)将旧的非TAP异步模式包装到TAP结构中。我不明白的是,为什么他只返回TCS对象的Task属性而不是等待它TCS.Task?

下面是示例代码:

旧的包装方法是DownloadString(.):

代码语言:javascript
复制
public interface IMyAsyncHttpService
{
    void DownloadString(Uri address, Action<string, Exception> callback);
}

将其封装到TAP结构中:

代码语言:javascript
复制
public static Task<string> DownloadStringAsync(
    this IMyAsyncHttpService httpService, Uri address)
{
    var tcs = new TaskCompletionSource<string>();
    httpService.DownloadString(address, (result, exception) =>
    {
        if (exception != null)
            tcs.TrySetException(exception);
        else
            tcs.TrySetResult(result);
    });
    return tcs.Task;
}

现在为什么不这样做呢:

代码语言:javascript
复制
public static async Task<string> DownloadStringAsync(
    this IMyAsyncHttpService httpService, Uri address)
{
    var tcs = new TaskCompletionSource<string>();
    httpService.DownloadString(address, (result, exception) =>
    {
        if (exception != null)
            tcs.TrySetException(exception);
        else
            tcs.TrySetResult(result);
    });
    return await tcs.Task;
}

两者之间有功能上的区别吗?第二个不是更自然吗?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2019-02-18 11:40:16

通过标记它异步,编译器将生成警告,认为它应该等待此方法。

您不必将自己的方法标记为async,以获得“任务未等待”警告。下面的代码为对TU的调用生成相同的警告

代码语言:javascript
复制
static async Task Main(string[] args)
{
    Console.WriteLine("Done");
    T();
    U();
    Console.WriteLine("Hello");
}

public static Task T()
{
    return Task.CompletedTask;
}

public static async Task U()
{
    await Task.Yield();
    return;
}

每当您发现自己使用的方法只包含单个await,并且这是它所做的最后一件事(除了可能返回所期待的值)时,您应该问自己它正在添加什么值。除了异常处理上的一些差异之外,它只是在混合中添加了一个额外的Task

await通常是一种表示“我现在没有有用的工作要做,但当这个其他的Task完成时”的方式,当然这不是真的(您以后没有其他的工作要做)。所以跳过await,只返回你所期待的东西。

票数 1
EN

Stack Overflow用户

发布于 2019-02-18 11:03:11

您的版本更加复杂--而不是仅仅返回任务,而是创建方法异步,等待您可能返回的任务。

票数 0
EN

Stack Overflow用户

发布于 2019-02-18 11:08:24

有一个微妙的实际差异(除了await运行速度较慢的版本外)。

在第一个示例中,如果DownloadString抛出一个异常(而不是调用委托,而是使用exception set传递它),那么该异常将通过对DownloadStringAsync的调用而出现气泡。

在第二个过程中,异常被打包到从Task返回的DownloadStringAsync中。

因此,假设DownloadString抛出此异常(并且没有发生其他异常):

代码语言:javascript
复制
Task<string> task;
try
{
    task = httpService.DownloadStringAsync(...);
}
catch (Exception e)
{
    // Catches the exception ONLY in your first non-async example
}

try 
{
    await task;
}
catch (Exception e)
{
    // Catches the exception ONLY in your second async example
}

你可能不在乎区别--如果你只写:

代码语言:javascript
复制
await httpService.DownloadStringAsync(...);

你不会注意到区别的。

同样,只有当DownloadString方法本身抛出时才会发生这种情况。如果它调用您将exception设置为值的委托,那么两种情况之间没有明显的差别。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54745531

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档