【发布时间】:2012-03-08 10:21:14
【问题描述】:
想象一个应用程序的功能需要多达 5 个线程处理数据,这些线程使用缓冲区、互斥体和事件相互交互。性能至关重要,语言是 C++。
该功能可以作为一个(编译)单元和一个类来实现,并且只能为应用程序实例化该类的一个实例。该类本身在 run() 方法中实现了 1 个线程,该方法产生其他 4 个线程,管理它们并在用户关闭应用程序时收集它们。
选择以下方法之一比另一种方法有什么优势(请告诉我任何更好的方法)?
- 向类中添加 5 个静态方法,每个方法运行一个线程、互斥锁和其他数据作为静态类变量共享。
- 添加5个全局函数(无作用域)并使用全局变量、事件和互斥锁(好像是C)
- 完全改变模式,再添加 4 个类,每个类实现一个线程并通过全局变量共享数据。
以下是一些需要考虑的想法和问题(如有错误请更正):
- 将线程作为类成员(当然是静态的),它们可以依靠单例来访问非静态成员函数,它还为它们提供了一个命名空间,这本身似乎是个好主意。
- 使用静态类方法,类头文件很快将包含许多静态变量(和其他辅助静态方法)。必须在类头文件中声明变量可能会给包含头文件的其他单元带来额外的依赖关系。如果变量在全局范围内声明,它们可以隐藏在单独的头文件中。
- 静态类变量应该在代码的某个地方定义,所以它会加倍输入声明的东西。
- 编译器可以利用命名空间解析来优化代码(与可能在不同单元中的全局变量相反)。
- 单个单元可能会得到更好的优化,而整个程序的优化速度很慢并且可能效果不佳。
- 如果单元增长,我必须将部分代码移动到一个单独的单元,所以我将有一个包含多个(编译)单元的类,这是否是反模式?
- 如果使用多个类,每个类处理一个线程,同样的问题可以在静态方法和全局函数之间做出决定来实现线程。此外,这需要更多的代码留置权,这不是一个真正的问题,但值得额外的开销吗?
请在没有 Qt 等库的情况下回答这个问题,然后假设我们可以依赖 QThread 并为每个 run() 方法实现一个线程。
Edit1:每个设计的线程数是固定的,数字 5 只是一个示例。请分享您对方法/模式的想法,而不是细节。
Edit2:我发现this answer(对另一个问题)非常有帮助,我猜第一种方法滥用类作为命名空间。如果与命名空间结合使用,则可以减轻第二种方法。
【问题讨论】:
-
我不喜欢计划 1、2 或 3。它们似乎都有神奇的数字“5”(或 5-1)。这开始散发出线程微管理和缺乏灵活性的味道。如果性能至关重要,那么如何拼凑代码并不重要——重要的是如何拼凑数据。你在操作什么数据?是否可以简单地在线程之间进行划分以减少锁定和错误共享?输入数据从哪里来,输出到哪里去?
-
+1 @MartinJames - Martin 是对的:您以错误的方式思考问题。您需要决定“如何以独立计算的方式划分数据”?考虑输入和输出也很重要:如果你做对了,你甚至可能不需要锁。我强烈建议您构建一个简化模型并对其进行彻底测试,以确保您在真正实施之前了解线程和锁定的工作原理。
-
@MartinJames - 感谢您的回答,实际上在一个项目中我正在使用方法 1。它具有网络、视频捕获、编码、解码、跟踪和渲染功能。假设一个 4 核处理器,我已经划分了任务,它是如何神奇数字 5 来的。同时,我使用 IPP、OpenMP 和 OpenGL 直接渲染,我可以得到低于 3ms 的帧,这意味着一个很好的 fps。我有环形缓冲区,只有当其中一个线程落后时才使用锁,所以它几乎是无锁的。
-
@kfmfe04 - 应用程序工作正常,只是现在我有一个胖 cpp 文件,一个头文件,正如我所说,包括它会给其他单元带来一些依赖性,减少类的相互依赖性。我还有其他一些使用第二种方法的项目,但对我来说,它们是伪装成 C++ 的 C。我也有一些 Java 项目,其方法类似于第三种方法。所以我想知道这些模式背后的基本原理是什么,以及其他人使用的是什么。
标签: c++ multithreading qt static-methods