你这样做是错的…(翻译文)

本文翻译自: https://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/
作者: Bradley T. Hughes

  我们广泛使用IRC(网上交谈)与我们自己以及社区进行沟通。我在Freenode网络的Qt频道上闲逛,尽我所能帮助别人。

  我看到的一个常见问题(这让我同时感到害怕)与理解Qt的线程以及如何编写一些他们编写的代码有关。人们根据他们的代码展示他们的代码或示例,我经常最终会想到:

你这样做是错的

  我知道这有点大胆,或许有点挑衅,但与此同时,我不禁认为下面的(假设的)类是面向对象原则的不正确应用以及Qt的使用不正确。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyThread : public QThread
{
public:
MyThread()
{
moveToThread(this);
}

void run();

signals:
void progress(int);
void dataReady(QByteArray);

public slots:
void doWork();
void timeoutHandler();
};

  我对这段代码的最大的不满是moveToThread(this),我看到很多人在不了解它的情况下使用它。你问,它做了什么?moveToThread()函数告诉Qt确保从指定的线程上下文中调用事件处理程序以及扩展的信号和槽。

  QThread是线程接口,所以我们告诉线程“自己”运行代码。我们也在线程运行之前进行此操作。尽管这看起来可行,但它让人困惑,而不是QThread的设计用途(QThread中的所有函数都是从创建线程而不是QThread启动的线程中编写和调用的)。

  我的印象是,moveToThread(this)。因为他们在某个地方看到了一些使用它的博客。一个快速的网络搜索出现了这些博客中的几个,所有这些都遵循上面类中的模式

  • 子类QThread
  • 添加信号和插槽以完成工作
  • 测试代码,看到插槽没有“从正确的线程”调用
  • 问Google,找到moveToThread(this),并评论“当我添加这个时它似乎工作”

  在我看来,问题始于moveToThread(this)。QThread的设计目的是用作操作系统线程的接口或控制点,而不是用于放置要在线程中运行的代码的位置。我们面向对象的程序是子类,因为我们想要扩展或专门化基类功能。

  我可以想到的继承QThread的唯一有效理由是添加QThread没有的功能,例如可能提供指向内存的指针以用作线程的堆栈,或者可能添加实时接口支持。不应将用于下载文件,查询数据库或进行任何其他类型处理的代码添加到QThread的子类中,它应该封装在它自己的对象中。

  通常,这意味着只需将类更改为继承自QObject而不是QThread,并且可能更改类名。QThread有一个started()信号,您可以在需要执行某些初始化时连接到该信号。要实际让代码在新线程上下文中运行,您需要实例化QThread并使用该moveToThread()函数将对象分配给该线程。

  即使您仍在使用moveToThread()告诉Qt在特定线程上下文中运行代码,我们仍然将线程接口保持独立。如有必要,现在可以将您的类的多个实例分配给单个线程,或者将多个不同类的多个实例分配给单个线程。换句话说,没有必要将类的单个实例绑定到单个线程。

  我对编写线程化Qt代码所带来的困惑负有很大责任。原始的QThread类是抽象的,因此子类化是必要的。直到Qt4.4,QThread::run()才获得默认实现。

  以前,使用QThread的唯一方法是子类。随着线程关联的增加以及对不同关联对象之间的信号和槽连接的支持,我们突然有了一种处理线程的方便方法。我们喜欢方便,我们想用它。不幸的是,我后来才意识到,强迫人们将继承QThread实际上使它变得比需要的更难。

最后

  • 文章有删减
  • 公众号后台回复”翻译“获取更多相关内容。