有关线程,可以做的是:
在QThread子类添加信号。这是绝对安全的,并且也是正确的(前面我们已经详细介绍过,发送者的线程依附性没有关系)
不应该做的是:
调用moveToThread(this)函数
指定连接类型:这通常意味着你正在做错误的事情,比如将QThread控制接口与业务逻辑混杂在了一起(而这应该放在该线程的一个独立对象中)
在QThread子类添加槽函数:这意味着它们将在错误的线程被调用,也就是QThread对象所在线程,而不是QThread对象管理的线程。这又需要你指定连接类型或者调用moveToThread(this)函数
使用QThread::terminate()函数
不能做的是:
在线程还在运行时退出程序。使用QThread::wait()函数等待线程结束
在QThread对象所管理的线程仍在运行时就销毁该对象。如果你需要某种“自行销毁”的操作,你可以把finished()信号同deleteLater()槽连接起来
什么时候应该使用线程
WebKit 发起一次请求
从服务器响应获取一些数据
WebKit 利用到达的数据开始进行网页布局,阻塞事件循环
由于事件循环被阻塞,也就没有了可用的事件循环,于是操作系统接收了到达的数据,但是却不能从QNetworkAccessManager的 socket 读取
内核缓冲区被填满,传输速度变慢
由于QNetworkAccessManager和QNetworkReply都是QObject,所以它们都不是线程安全的,因此你不能将它们移动到另外的线程继续使用。因为它们可能同时有两个线程访问:你自己的和它们所在的线程,这是因为派发给它们的事件会由后面一个线程的事件循环发出,但你不能确定哪一线程是“后面一个”。
Qt 4.8 之后,QNetworkAccessManager默认会在一个独立的线程处理 HTTP 请求,所以导致 GUI 失去响应以及操作系统缓冲区过快填满的问题应该已经被解决了。
最好最简单的实现是使用定时器,比如QTimer,设置 1s 超时,然后将doWork()作为槽:
class Worker : public QObject
{
Q_OBJECT
public:
Worker()
{
connect(&timer, SIGNAL(timeout()), this, SLOT(doWork()));
timer.start(1000);
}
private slots:
void doWork()
{
/* ... */
}
private:
QTimer timer;
};