【问题标题】:Should I use Tasks or Threads for this use case我应该为这个用例使用任务还是线程
【发布时间】:2014-03-28 16:07:04
【问题描述】:

我仍在使用 .NET 4 多线程库,所以我不确定它们在这里是否比较低级别的解决方案更有帮助。

我有一个 BlockingCollection 需要发送到某个 HTTP 服务端点的消息。通常我的解决方案是像这样创建一个无限循环函数

void DoWork()
{
    while(true)
    {
        var message = collection.Take();
        SendMessage();
    }
}

启动 X 个 Thread() 对象来工作循环。多线程考虑了等待 HTTP 响应所花费的时间,然后我会分析和调整性能以确定要使用的正确线程数。

这是否更适合任务?

【问题讨论】:

  • 考虑使用异步 IO。完全没有必要捆绑等待IO的线程。 TPL 默认使用 ThreadPool,如果您将大量长期阻塞作业放入其中,该线程池将很快耗尽。
  • 不要使用工作线程。使用自然异步 API,例如HttpClient,喜欢this

标签: c# .net multithreading


【解决方案1】:

在这种情况下使用 Task 或 Thread 没有太大区别。两者都会做同样的事情。

我宁愿用GetConsumingEnumerable 写这个,这样当没有元素时它就会出来。

void DoWork()
{
    foreach(var message in collection.GetConsumingEnumerable())
    {
        SendMessage();
    }
}

【讨论】:

  • 我不希望它退出。消息是由用户输入生成的,一秒钟可能有 10000 条消息,也可能是 10000 秒中有一条消息。我希望这件事耐心等待一些人出现,但永远不要停止。
  • @PiersMacDonald GetConsumingEnumerable 将永远不会返回,直到您致电 blockingCollection.CompleteAdding 直到那时它才会阻塞。阅读GetConsumingEnumerable
  • 对,但随后 foreach 将运行并且函数将退出。这不是 OP 想要的。
  • @EdS。你应该阅读GetConsumingEnumerable。它不会返回控制权。在您处理集合或调用CompleteAdding 之前,它将阻塞并且永远不会退出该函数。我的代码在语义上等于 Op 的代码。如果不调用CompleteAdding,则此方法永远不会结束。这就是GetConsumingEnumerable的美妙之处
  • 啊,谢谢。我认为它的行为类似于正常的固定可枚举,它会在结束后返回。很高兴知道,谢谢!我想我本可以使用一段时间(!collection.IsCompleted),但我永远不希望这件事停下来。
【解决方案2】:

我不打算就你应该使用哪一个提供我的意见,但我会提供一些指导。

这篇文章:https://stackoverflow.com/a/4130204/3317555 提供了一些非常好的指导。由于您正在跟上 .NET 4 的速度,因此您可能想同时尝试两种方式,看看在功能方面您更喜欢哪种风格以及它在代码中的外观。

编辑:我会这么说,不要使用 Parallel。

【讨论】:

  • 根据我的经验,Parallel 习惯使用我所有的 CPU 内核,这是它的设计目的。这会降低 UI 的响应速度。另外,请快速浏览the exposed methods of the class。从您的帖子中,您似乎正在寻找async 方法。并行非常适合完成工作,但不适用于等待。
【解决方案3】:

来自 MSDN -“从 .NET Framework 4 开始,TPL 是编写多线程和并行代码的首选方式。”

http://msdn.microsoft.com/en-us/library/dd460717.aspx

我发现 Parallel.ForEach() 擅长此类任务。它优化了线程数。它说明了系统中的核心数量以及线程在做什么。

【讨论】:

    猜你喜欢
    • 2012-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-02
    • 1970-01-01
    • 2011-06-02
    • 2015-09-14
    相关资源
    最近更新 更多