【问题标题】:Any disadvantage to creating a class which uses methods which return a Task<T> but are synchronous methods over methods returning T创建一个类的任何缺点,该类使用返回 Task<T> 但与返回 T 的方法相比是同步方法
【发布时间】:2021-01-07 18:21:09
【问题描述】:

假设我们有这个类 - MyClass

MyClass 具有将不同类型的列表转换为 XDocument 的方法

作为示例,我们将只看一种方法,Convert

我可以把这个方法写成:

public XDocument Convert<T>(IList<T> myList)
{
    XDocument doc = new XDocument("root");
    //Do some synchronous work       
    return doc;
}

或者我可以这样写:

public Task<XDocument> Convert<T>(IList<T> myList)
{
    XDocument doc = new XDocument("root");
    //Do some synchronous work       
    return Task.FromResult(doc);
}

现在假设我们想将其卸载到程序另一部分的任务中,而其他一些任务正在做一些工作,然后等待它们全部完成,然后再在异步方法中做一些其他工作。

我知道使用该方法的第一个版本,我可以做到:

some async method...

Task t = Task.Run(() => myClass.Convert<T>(IList<T>);
Task t2...
Task t3...
await Task.WhenAll(t, t2, t3);

但是使用第二种形式的方法,似乎可以做到更简单:

some async method...

Task t = myClass.Convert<T>(IList<T>);
Task t2...
Task t3...
await Task.WhenAll(t, t2, t3);

我的问题是:

像第二个例子那样设计方法有什么缺点吗?返回一个任务以便在代码库的其他地方作为任务运行时使用,即使它是同步的。还是这更多是关于意图?

我知道这可能被认为是一个固执己见的问题,所以在我给它足够的时间来获得一些回应之后,我可能会将它从 SO 中删除。否则,如果考虑到一个足够具体和好的问题,我会留下它。

【问题讨论】:

  • 如果您不等待,则无需使用Tasks,除非您有多个实现并需要匹配一个接口。 Task.Run 可以吞下错误,因此请确保在它调用的代码中处理它们。除非您从磁盘加载文档,否则我看不出在当前示例中使用这两种情况的原因。在您需要异步功能之前,我会将其保留为非任务。 Async/await 确实增加了一些额外的开销。不过,对于代码审查网站来说,这可能是一个更好的问题。
  • @ps2goat 假设我们在代码库的另一部分,在一个异步方法中,我们希望将列表转换为 XDocument 的工作转移到一个任务上,并且可能正在做一些工作一些其他任务,然后等待所有这些任务完成,然后再在该方法中执行更多操作。这是否更清楚地说明了为什么将 Convert 方法设计为返回 Task
  • 所以你认为更改方法的签名以返回Task&lt;T&gt; 而不是TTask.Run 方法相同吗?如果你这样做了,那你就错了。完全有可能实现具有异步合约和同步实现的行为不良的方法。一种方法不是将不完整的Task 交给调用者,而是强制调用者同步完成所有工作,最后将完整的Task 交还给调用者。这就是第二个Convert 本质上在做的事情。
  • 所有三个任务tt1t2 将以完成状态创建。然后组合任务Task.WhenAll(t, t2, t3) 也将在创建时立即完成。简而言之,什么都不会a同步发生。当前线程将简单地一个接一个地执行所有三个Convert调用,就像Convert返回一个XDocument而不是Task&lt;XDocument&gt;一样。
  • @TheodorZoulias 谢谢。那是我不清楚并希望了解的部分。我想删除这篇文章,但它不会让我,因为有人在下面提供了答案。

标签: c# oop


【解决方案1】:

像第二个例子那样设计方法有什么缺点吗?

是的。该方法是同步的,但它具有异步签名。这是一个糟糕的设计选择。

这很容易导致意外误用。比如说……

做起来似乎更简单

该代码不会像您想象的那样工作。这些任务不会并行运行。因为代码是同步的;它只是有一个异步签名

如果您考虑使用Task.Run 作为伪异步方法的实现,that's also considered a bad practice

【讨论】:

  • 谢谢。这和最后一届 cmets 给出的都是我一直在寻找的。我只需要有人向我展示所指出的内容,“我的代码没有按照我的想法做”。再次感谢!
  • 我阅读了您为我发布的那篇文章,谢谢。听起来好像我想并行做一些工作,而我的类方法的唯一选择是同步的,那可能是使用 Task.Run() 的时候了,还是我完全看错了?
  • 谢谢。我读了你的帖子,我认为这对我来说更清楚了,感谢你花时间向我解释。
  • 在收到您的反馈并查看我在代码中所做的事情后,我觉得有点愚蠢,我只是将一个 Task 变量设置为一个已完成的 Task 并且方法是只是在下一个任务声明之前被调用并完成。我不知道我为什么要在这个问题上留出时间,但再次感谢您的时间和解释。
【解决方案2】:

你的两个例子没有做同样的事情。在第一个示例中,您使用Task.Run,您将工作转移到线程池以并行执行。异步任务不使用线程池;他们使用协作多任务来并行完成异步操作。

【讨论】:

  • 我知道Task.Run 将工作移到线程池线程上,我的问题更多是关于设计类方法是否返回Task&lt;T&gt; 以用于异步方法,而其他任务正在执行其他任务独立工作,然后等待所有这些方法在类中设计这些方法是否不好?不过,我非常感谢您抽出时间做出贡献。
猜你喜欢
  • 1970-01-01
  • 2014-08-14
  • 1970-01-01
  • 2015-05-20
  • 2016-09-23
  • 1970-01-01
  • 2022-06-21
  • 2016-10-05
  • 1970-01-01
相关资源
最近更新 更多