共计 1541 个字符,预计需要花费 4 分钟才能阅读完成。
本文翻译自: https://woboq.com/blog/qthrea…
原作者: Olivier Goffart
发布时间:2013 年 1 月 22 日
这篇文章是关于 QThread 的使用的。这是对我当时的同事 Brad 三年前的博客帖子的回答:” 您做错了 ”。
Brad 在他的博客文章中解释说,他看到许多用户通过对 QThread 进行子类化,在该子类中添加一些槽并在构造函数中执行以下操作来滥用 QThread:
moveToThread(this);
他们把线程移动到自己类内。正如 Brad 所提到的,这是错误的:QThread 应该是管理线程的接口。因此,应该在创建线程中使用它。
这样,就无法在该线程中运行 QThread 对象中的槽,并且在 QThread 的子类中具有槽是一种不好的做法。
但是,Brad 继续并完全不鼓励使用 QThread 的任何子类。他声称这违反了正确的面向对象设计。这是我不同意的地方。放入代码 run()
是扩展 QThread 的一种有效的面向对象方法:QThread 表示一个仅启动事件循环的线程,子类表示一个被扩展以执行其工作的线程run()
。
Brad 上任后,该社区的一些成员就反对对 QThread 进行子类化进行了讨伐。问题在于,有很多完全合法的原因可以继承 QThread。
在 Qt 5.0 和 Qt 4.8.4 中,更改了 QThread 的文档,因此示例代码不涉及子类。查看 Qt 4.8 QThread 文档的第一个代码示例(更新的文档已经修复)。它具有许多样板行,仅用于在线程中运行一些代码。而且甚至存在泄漏:QThread 永远不会退出并被销毁。
我在 IRC 上被问到一个用户的问题,该用户遵循该示例,以便在线程中运行一些简单的代码。他很难弄清楚如何正确销毁线程。这就是促使我撰写此博客条目的原因。
如果允许子类化 QThread,那么您将获得:
class WorkerThread : public QThread {void run() {// ...}
};
void MyObject::startWorkInAThread()
{
WorkerThread *workerThread = new WorkerThread;
connect(workerThread, SIGNAL(finished()),
workerThread, SLOT(deleteLater()));
workerThread->start();}
此代码不再泄漏,并且更加简单,并且不会创建无用的对象,因此开销较小。
Qt 线程示例 threadedfortuneserver 是使用此模式运行阻塞操作的示例,并且比使用 worker 对象的等效示例要简单得多。
我已经向文档提交了补丁,以免再次阻止对 QThread 的子类化。
经验法则
什么时候子类化,什么时候不子类化?
- 如果您确实 不需要 线程中的 事件循环 ,则应该 子类化。
- 如果需要 事件循环 并处理线程中的信号和槽,则可能 不需要子类化。
改用 QtConcurrent 呢?
QThread 的级别很低,您最好使用更高级别的 API,例如 QtConcurrent。
现在,QtConcurrent 有其自身的一系列问题:它与单个线程池绑定,因此如果要运行阻塞操作,它不是一个好的解决方案。在其实现中还存在一些问题,这些问题会带来一些性能开销。所有这些都是可以修复的。也许甚至 Qt 5.1 也会有所改进。
一个很好的选择也是 C ++ 11 与标准库 std::thread 和 std::async 它们现在在一个线程中运行的代码的标准方式。好消息是它仍然可以在 Qt 上正常工作:所有其他 Qt 线程原语都可以与本机线程一起使用。(如果需要,Qt 将自动创建一个 QThread 来创建)
关于更多
- 请查看往期文章「你这样做是错的…(翻译文)」