【问题标题】:Task.Run on method returning T VS method returning Task<T>Task.Run on 方法返回 T VS 方法返回 Task<T>
【发布时间】:2018-06-05 13:26:24
【问题描述】:

场景 1 - 对于字符串列表 (_websites) 中的每个网站,调用者方法将 GetWebContent 包装到一个任务中,等待所有任务完成并返回结果。

    private async Task<string[]> AsyncGetUrlStringFromWebsites()
    {
        List<Task<string>> tasks = new List<Task<string>>();

        foreach (var website in _websites)
        {
            tasks.Add(Task.Run(() => GetWebsiteContent(website)));
        }

        var results = await Task.WhenAll(tasks);

        return results;
    }

    private string GetWebContent(string url)
    {
        var client = new HttpClient();

        var content = client.GetStringAsync(url);

        return content.Result;
    }

场景2 - 对于字符串列表(_websites)中的每个网站,调用者方法调用GetWebContent(返回Task),等待所有任务完成并返回结果。 p>

    private async Task<string[]> AsyncGetUrlStringFromWebsites()
    {
        List<Task<string>> tasks = new List<Task<string>>();

        foreach (var website in _websites)
        {
            tasks.Add(GetWebContent(website));
        }

        var results = await Task.WhenAll(tasks);

        return results;
    }

    private async Task<string> GetWebContent(string url)
    {
        var client = new HttpClient();

        var content = await client.GetStringAsync(url);

        return content;
    }

问题 - 哪种方法是正确的方法,为什么?每种方法对实现异步处理有何影响?

【问题讨论】:

  • 您询问了偏好,这是基于意见的。你应该问为什么第一个不正确而第二个是正确的?是什么让他们如此?
  • @LasseVågsætherKarlsen 我已经更新了问题

标签: c# .net async-await asynchronous


【解决方案1】:

使用Task.Run(),您从线程池中占用一个线程,并告诉它等待直到接收到 Web 内容。
你为什么想这么做?你会付钱让某人站在你的邮箱旁边告诉你什么时候收到信吗?

GetStringAsync 已经是异步的。当内容通过网络进入时,cpu 与(与此过程)无关。

所以第二种方法是正确的,这里不需要使用线程池中的额外线程。

阅读总是很有趣:Stephen Cleary's "There is no thread"

【讨论】:

  • 另外,Task.Result 的使用会导致死锁。
  • 场景2不是从线程池中占用一个线程并告诉它等待吗?请您扩展您的答案。为什么场景 1 使用的线程比场景 2 多?
  • @GIVE-ME-CHICKEN 不,第二个版本不使用任何额外的线程。请参阅我链接的博客文章,Stephen Cleary 的解释比我写的要好得多。 awaiting 真正 异步GetStringAsync 不会阻塞或使用任何线程,它只是返回调用者并在GetStringAsync 返回的Task 完成后恢复方法的其余部分。
【解决方案2】:

@René Vogt 给出了很好的解释。

我这边还有 5 美分。

在第二个例子中,GetWebContent 方法中不需要使用async / await。您可以简单地返回Task&lt;string&gt;(这也会减少async 的深度)。

【讨论】:

  • 除非HttpClient确实应该在使用后被丢弃,这种情况下他需要放回async/await。
  • HttpClient 是 IDisposable 类型,因此您可以将其处理掉,但建议不要这样做,以免引发 SocketExceptions。阅读aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong - 此外,除非您具有正确的 ServicePointManager 设置,否则将 HttpClient 作为单例不尊重 DNS 更改。 Asp Net Core 2.1 中的新 HttpClient 工厂旨在解决所有这些问题 - stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore
  • 如果您不打算重用客户端,仍然建议将其丢弃,文章详细说明的不仅仅是“不要丢弃 HttpClient”。
  • @GIVE-ME-CHICKEN 在这种情况下没关系:GetStringAsync() 返回Task&lt;string&gt;。你可以在你的async GetWebContent() 中await 那个Task,然后它也会返回一个Task&lt;string&gt;,或者你可以简单地返回Task&lt;string&gt; 并让调用者await 它。这为您节省了一定程度的间接性和编译器生成的额外异步状态机
  • @GIVE-ME-CHICKEN 是的,如果你想在返回之前操作GetWebContent 中的字符串,你需要string s = await GetStringAsync(); string res = DoSomething(s); return res; 并将GetWebContent 声明为async。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-23
  • 1970-01-01
  • 2014-08-14
  • 2016-10-05
  • 1970-01-01
  • 1970-01-01
  • 2015-01-01
相关资源
最近更新 更多