【问题标题】:Message-based multithreading or Thread Pool for a short and uncommon action?基于消息的多线程或线程池用于短暂且不常见的操作?
【发布时间】:2010-06-28 19:10:57
【问题描述】:

我目前在 .NET 中使用 Retlang 进行基于消息的多线程,这是一个很棒的库。我不再有显式锁定,每个线程都在做自己的事情,管理应用程序的一部分并通过消息与其他线程通信。

我现在必须实现我的应用程序的一项功能,该功能也可以有自己的发布者/订阅者线程。唯一的问题是这个线程实际上会做很少的工作。预计每 10 分钟左右会收到来自发布者的消息。收到消息后,它会做一些工作,但不会超过几百毫秒。

所以我开始怀疑让线程在 99.9% 的时间里处于休眠状态是否真的是一个不错的选择。这种操作有线程池,但由于我无法控制接收消息的线程,所以我不得不求助于丑陋的、容易出错的锁。

我的问题是:在资源方面,让线程闲置,等待绝大多数时间真的是个问题吗?在使用良好的基于​​消息的架构之后使用共享多线程感觉就像回到过去,而且它将是应用程序中唯一带有锁的部分。但我一直在想“我在这里做错了吗?”用这个线程。

编辑:谢谢大家,在阅读了你们的每一个答案后,我认为另一个线程不是什么大问题。我的应用程序将仅与基于消息的多线程保持一致,如果我真的有性能问题(但不应该是这样),我会进一步调查。

【问题讨论】:

    标签: .net multithreading locking publish-subscribe retlang


    【解决方案1】:

    我实际上会争辩说,将 ThreadPool 用于大部分时间都在休眠的线程是一个糟糕的设计选择 -

    让单个线程休眠(或者,更好的是,等待事件)在您的应用程序中的开销非常小。使用专用线程的主要缺点是线程需要分配自己的堆栈,因此与线程池线程相比,您将使用一些额外的内存。

    【讨论】:

    • 我不担心额外的内存,内存对于这种应用程序来说不是问题。我主要担心的是另一个线程对应用程序的潜在影响。
    【解决方案2】:

    这听起来像是使用Task 的理想方案。他们默认使用底层的线程池,但有一个更进化的 API。

    【讨论】:

    • 除非这将有资格成为使用 LongRunning 提示生成的任务的良好候选者(以防止 ThreadPool 中的线程饥饿)。但是,如果您这样做,您将生成一个专用线程(前提是您使用默认调度程序),因此 Task 除了可能更清洁的 API 之外不会提供任何优势。
    • 嗯,不;我的意思是为每项工作创建一个Task。所以每个Task 都将是非常短暂的。
    【解决方案3】:

    您不能为此使用 Retlang 的 PoolFiber 吗?它不是由像ThreadFiber 这样的专用线程支持的,而是由.Net 线程池支持的。这意味着您可以继续使用在整个应用程序中使用的相同 Retlang 语义,而无需保持空闲线程。

    【讨论】:

    • 我已经知道它并打算使用它。但是它仍然在随机线程池线程上产生消息,因此那里的工作必须与现有对象同步,在该线程上的工作之间共享,但不与其他工作共享。
    • 我看到了问题。这种类型的同步正是您要阻止的。但是不应该通过其他光纤对您隐藏对这些现有对象的直接访问吗? IE。您不应该再次使用消息与负责更新这些对象的光纤通信吗?
    • 这是我问题的核心。我遇到问题的线程是应用程序的一个独立部分,它当然会与其他纤程通信,但也有自己的对象,仅由这个纤程/线程使用,而不暴露在外面。
    【解决方案4】:

    虽然 PoolFiber 上的每个操作都可以在单独的池线程中执行,但特定 PoolFiber 的操作按顺序而不是并行执行,一次只能执行一个池线程。

    PoolFiber 应该可以满足您的需求。

    【讨论】:

    • 谢谢,我完全没有意识到这一点。你是格雷厄姆纳什,Retlang 的作者之一,对吧?感谢您的伟大图书馆! (虽然我发现当你像我一样没有 Erlang 或 Scala 的经验时,文档是缺乏的。)
    • 是的,就是我。 Mike Rettig 做了大部分工作,我只是维护它。现在 Retlang 1.0.1 已经发布,我将努力改进文档。有哪些特别需要改进的领域?
    【解决方案5】:

    我认为这基本上是一个优化问题。您的应用程序是否存在性能问题(特别是内存问题)?如果没有,那么继续让线程空闲并保持代码更干净。找到真正的理由后探索其他选择。

    【讨论】:

      【解决方案6】:

      我会说有一个专门的线程来接收这些消息很好。我什至可以说这将是首选方法。这不像您只是随意创建线程或类似的东西。我们在这里讨论的是一个额外的线程,它不会消耗很多资源(可能会占用一点堆栈空间)。在我看来,不必担心共享状态的额外同步(当然除了消息传递)的优势胜过劣势。

      【讨论】:

        【解决方案7】:

        您应该考虑使用 F#。它非常适合在不烧线程的情况下对逻辑单线程代理进行编程(例如,代理可以在 ThreadPool 上跳跃,但仍以序列化的方式响应消息,并且其邮箱中到达的消息会唤醒它们并安排 ThreadPool 工作)。

        http://blogs.msdn.com/b/dsyme/archive/2010/02/15/async-and-parallel-design-patterns-in-f-part-3-agents.aspx

        【讨论】:

        • 整个应用程序已经在 C# 中,虽然我很想学习 F#,但我目前没有时间学习它,我不认为实现其中的一小部分用另一种语言申请是值得的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-02-04
        • 1970-01-01
        • 2022-11-02
        • 2018-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多