【问题标题】:Is Task.Run or Task.Factory.StartNew are bad practice for windows phone or any other client platform?Task.Run 或 Task.Factory.StartNew 对于 windows phone 或任何其他客户端平台是不好的做法吗?
【发布时间】:2015-10-07 14:09:34
【问题描述】:

我有 WP 8.1 应用程序,它经常使用 Web 服务,我希望它尽可能地响应。来自 iOS 开发经验 - 有一条严格的规则:“不要在 UI 线程中进行任何复杂的计算!不管你如何达到这个目的:使用块或使用 GCD”。

并非所有 API 都有异步版本的问题,例如应用程序中的 JSON.NET、SQLite 或任何自己的复杂算法。我读过很多 articles 将 Task.Run 和 Task.Factory.StartNew 定义为不好的做法 + this

那么,这样写代码好吗?它会导致一些 cpu 过载/电池耗尽/稳定性问题吗?如果异步包装器是个坏主意 - 使复杂操作异步(后台线程)的正确方法是什么?

 protected async Task<T> GetDataAsync<T>(string uriString, CancellationToken cancellationToken = default(CancellationToken))
    {
        var uriToLoad = new Uri(uriString);
        using (var httpClient = new HttpClient())
        {
            var response= await httpClient.GetAsync(uriToLoad, cancellationToken).ConfigureAwait(false);
            response.EnsureSuccessStatusCode();
            cancellationToken.ThrowIfCancellationRequested();

            var dataString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
            cancellationToken.ThrowIfCancellationRequested();

            // async wrapped call to sync API
            var result = await Task.Factory.StartNew(() => JsonConvert.DeserializeObject<T>(dataString), cancellationToken).ConfigureAwait(false);
            cancellationToken.ThrowIfCancellationRequested();

            return result;
        }
    }

【问题讨论】:

  • Task.Run/Task.Factory.StartNew 适用于 CPU 密集型操作。对于没有“真正”异步 API 实现的 IO 绑定的,这是在后台执行某些操作的唯一方法,因为替代方法是在 UI 线程中执行相同操作。不要太明确。

标签: c# .net windows-runtime windows-phone async-await


【解决方案1】:

Stephen toub 的文章说,在 中公开同步方法的异步包装器是不好的。那没有任何价值。如果开发者需要,他可以随时拨打Task.Run(()=&gt; SomeSynchronousMethod());。无需在库中公开它。如果它是一个应用程序,是的,你可以,因为这是你可以减轻工作负担的唯一方法。

同样适用于“Newtonsoft.Json”。因为它是一个图书馆。

Task.Run 或 Task.Factory.StartNew 对于 windows phone 来说是不是一种不好的做法 或任何其他客户端平台?

是和不是。

如果你有一些繁重的计算要做。说图像处理或类似的东西。无论如何,您都必须这样做;它需要一些 CPU 周期。无论你运行什么线程,它都需要一些 cpu 时间。这意味着您必须消耗cpu,当然还有电池。

Task.Run 或 StartNew 坏了吗?

当您有 CPU 密集型工作时,情况并非如此。这就是他们的目的。

如果你需要完成你的工作,你需要一个线程,它不能是 UI 线程(我们需要它是响应式的)。因此,使用 Task.Run 或 StartNew 借用 ThreadPool 线程并没有错。

什么时候不好?

如果您在可以使用自然异步 API 时使用它,那就不好了。网络 I/O、文件 I/O 等本质上是异步的。 You don't need a thread to invoke truly asynchronous operation.

使用线程进行 IO 只是浪费线程,等待底层设备完成 IO 之前什么都不做。

简而言之,Task.Run 用于 CPU 绑定和自然异步 API 用于 IO 绑定操作。

【讨论】:

    【解决方案2】:

    我相信您误解了您所引用的文章(Stephen Toub 的)。那篇文章一般不讨论Task.Run()等的使用。相反,他评论的是是否应该将其基本同步操作包装在任务中,并将其作为库 API 的一部分公开。

    来自 Toub 的文章:

    如果开发人员需要使用同步 API 实现响应性或并行性,他们可以简单地使用 Task.Run 之类的方法包装调用

    即用Task.Run() 或类似的东西包装一个同步API 也不错,只要客户端 代码的开发者可以选择。


    至于如果您作为客户端代码的开发人员这样做会产生什么影响,这完全取决于 API。

    首先,对于固有的异步操作,使用已经支持的 API 绝对是更可取的。例如,网络 I/O。任何提供对网络协议的高级访问的体面的库都应该提供某种异步 API,如果不是 async 兼容的话。

    但是,如果您不得不使用只有同步方法的库,即使是用于 I/O 或其他类型的异步行为,您实际上可以使用异步调用来包装这些方法。

    它会导致一些 cpu 过载/电池耗尽/稳定性问题吗?

    在任何情况下,我都不希望出现此类重大问题。如果操作涉及 I/O,那么在等待数据实际到达时 CPU 不会很忙。对于其他任务,将工作卸载到后台线程只需要很少的开销;假设确实需要首先将工作排除在 UI 线程之外,那么工作本身在 CPU 方面的成本非常高,管理后台任务的额外 CPU 利用率将可以忽略不计。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-23
      • 1970-01-01
      • 1970-01-01
      • 2019-10-17
      • 1970-01-01
      相关资源
      最近更新 更多