QThread的Qt文档说要从QThread创建一个类,并实现run方法。
下面是摘自4.7Qthread文档...
运行创建您自己的线程,子类QThread并重新实现
()。例如:
class MyThread : public QThread
{
public:
void run();
};
void MyThread::run()
{
QTcpSocket socket;
// connect QTcpSocket's signals somewhere meaningful
...
socket.connectToHost(hostName, portNumber);
exec();
}
因此,在我创建的每个线程中,我都做到了这一点,而且在大多数情况下,它都工作得很好(我没有在我的任何对象中实现moveToThread(这),它工作得很好)。
我上周遇到了一个问题(通过绕过我创建对象的位置来设法通过它),并找到了following blog post。这里基本上是说,将QThread子类化确实不是正确的方法(文档也是不正确的)。
这是来自Qt开发人员的,所以乍一看我很感兴趣,经过进一步的思考,同意他的观点。遵循面向对象的原则,你真的只想子类化一个类来进一步增强该类……不仅仅是直接使用类方法……这就是为什么要实例化..。
假设我想要将一个自定义QObject类移动到一个线程...做这件事的“正确”方法是什么?在那篇博文中,他“说”他在某处有一个例子...但是如果有人能进一步给我解释一下,我会非常感激的!
更新:
由于这个问题引起了如此多的关注,这里是4.8文档的副本和粘贴,其中包含实现QThread的“适当”方法。
class Worker : public QObject
{
Q_OBJECT
QThread workerThread;
public slots:
void doWork(const QString ¶meter) {
// ...
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString)));
connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
我仍然认为值得指出的是,它们包含了一个额外的Worker::workerThread
成员,这是不必要的,并且在示例中从未使用过。去掉那段代码,它就是如何在Qt中进行线程化的一个恰当的例子。
发布于 2010-11-04 10:51:17
我能想到的唯一补充就是进一步声明QObject
与单个线程具有亲和力。这通常是创建QObject
的线程。因此,如果您在应用程序的主线程中创建QObject
并希望在另一个线程中使用它,则需要使用moveToThread()
来更改亲和性。
这就省去了对QThread
进行子类化和在run()
方法中创建对象的麻烦,从而使您的东西能够很好地被封装。
这篇博文确实包含了一个example的链接。它非常简短,但它展示了基本的思想。创建QObject
,连接信号,创建QThread
,将QObjects
移动到QThread
并启动线程。信号/槽机制将确保正确和安全地跨越线程边界。
如果必须在该机制之外调用对象上的方法,则可能必须引入同步。
我知道除了线程之外,Qt还有其他一些很好的threading facilities,可能值得熟悉,但我还没有这样做:)
发布于 2013-08-17 06:07:41
这里是one example of how to use QThread correctly,但它有一些问题,这反映在评论中。特别是,由于插槽的执行顺序没有严格定义,因此可能会导致各种问题。2013年8月6日发表的这篇评论给出了一个很好的想法来处理这个问题。我在我的程序中使用了类似的东西,这里有一些示例代码来澄清。
基本思想是相同的:我创建一个驻留在我的主线程中的QThread实例,一个驻留在我创建的新线程中的worker类实例,然后我连接所有信号。
void ChildProcesses::start()
{
QThread *childrenWatcherThread = new QThread();
ChildrenWatcher *childrenWatcher = new ChildrenWatcher();
childrenWatcher->moveToThread(childrenWatcherThread);
// These three signals carry the "outcome" of the worker job.
connect(childrenWatcher, SIGNAL(exited(int, int)),
SLOT(onChildExited(int, int)));
connect(childrenWatcher, SIGNAL(signalled(int, int)),
SLOT(onChildSignalled(int, int)));
connect(childrenWatcher, SIGNAL(stateChanged(int)),
SLOT(onChildStateChanged(int)));
// Make the watcher watch when the thread starts:
connect(childrenWatcherThread, SIGNAL(started()),
childrenWatcher, SLOT(watch()));
// Make the watcher set its 'stop' flag when we're done.
// This is performed while the watch() method is still running,
// so we need to execute it concurrently from this thread,
// hence the Qt::DirectConnection. The stop() method is thread-safe
// (uses a mutex to set the flag).
connect(this, SIGNAL(stopped()),
childrenWatcher, SLOT(stop()), Qt::DirectConnection);
// Make the thread quit when the watcher self-destructs:
connect(childrenWatcher, SIGNAL(destroyed()),
childrenWatcherThread, SLOT(quit()));
// Make the thread self-destruct when it finishes,
// or rather, make the main thread delete it:
connect(childrenWatcherThread, SIGNAL(finished()),
childrenWatcherThread, SLOT(deleteLater()));
childrenWatcherThread->start();
}
一些背景知识:
ChildProcesses类是一个子进程管理器,它通过调用spawn()启动新的子进程,保存当前正在运行的进程的列表,等等。但是,它需要跟踪子进程的状态,这意味着在Linux上使用waitpid()调用,在Windows上使用WaitForMultipleObjects。我过去常常使用计时器在非阻塞模式下调用它们,但现在我想要更迅速的反应,这意味着阻塞模式。这就是线程的用武之地。
ChildrenWatcher类的定义如下:
class ChildrenWatcher: public QObject {
Q_OBJECT
private:
QMutex mutex;
bool stopped;
bool isStopped();
public:
ChildrenWatcher();
public slots:
/// This is the method which runs in the thread.
void watch();
/// Sets the stop flag.
void stop();
signals:
/// A child process exited normally.
void exited(int ospid, int code);
/// A child process crashed (Unix only).
void signalled(int ospid, int signal);
/// Something happened to a child (Unix only).
void stateChanged(int ospid);
};
下面是它的工作原理。启动所有这些内容后,将调用ChildProcess::start()方法(参见上文)。它创建一个新的QThread和一个新的ChildrenWatcher,然后将其移动到新的线程中。然后,我连接了三个信号,这三个信号通知我的管理器它的子进程的命运(已退出/已发出信号/天知道发生了什么)。然后开始主要的乐趣。
我将QThread:: started ()连接到ChildrenWatcher::watch()方法,以便在线程就绪时立即启动它。因为监视器位于新线程中,所以watch()方法就是在这个线程中执行的(队列连接用于调用插槽)。
然后,我使用Qt::DirectConnection将ChildProcesses::stop()信号连接到ChildrenWatcher::stop()插槽,因为我需要异步执行此操作。这是必需的,所以当不再需要ChildProcesses管理器时,我的线程会停止。stop()方法如下所示:
void ChildrenWatcher::stop()
{
mutex.lock();
stopped = true;
mutex.unlock();
}
然后是ChildrenWatcher::watch():
void ChildrenWatcher::watch()
{
while (!isStopped()) {
// Blocking waitpid() call here.
// Maybe emit one of the three informational signals here too.
}
// Self-destruct now!
deleteLater();
}
哦,isStopped()方法只是在while()条件中使用互斥锁的一种方便方法:
bool ChildrenWatcher::isStopped()
{
bool stopped;
mutex.lock();
stopped = this->stopped;
mutex.unlock();
return stopped;
}
所以这里发生的事情是,我在需要完成时设置了stopped标志,然后下一次调用isStopped()时,它返回false,线程结束。
那么当watch()循环结束时会发生什么呢?它调用deleteLater(),因此一旦控制返回到线程事件循环,对象就会自毁,这恰好发生在deleteLater()调用之后(当watch()返回时)。返回到ChildProcesses::start(),您可以看到从监视器的destroyed()信号到线程的quit()槽有一个连接。这意味着当监视器完成时,线程会自动结束。当它完成时,它也会自毁,因为它自己的finished()信号连接到它的deleteLater()插槽。
这与Maya发布的想法基本相同,但因为我使用了自毁习惯用法,所以我不需要依赖于调用插槽的顺序。它总是先自毁,然后停止线程,然后再自毁。我可以在worker中定义一个finished()信号,然后将它连接到它自己的deleteLater(),但这只意味着多一个连接。由于我不需要将finished()信号用于任何其他目的,因此我选择只从worker本身调用deleteLater()。
玛雅还提到,你不应该在工作者的构造函数中分配新的QObjects,因为它们不会存在于你将工作者移动到的线程中。我会说无论如何都要这样做,因为这就是OOP的工作方式。只需确保所有这些对象都是worker的子级(即使用QObject(QObject*)构造函数)- moveToThread()将所有子级与正在移动的对象一起移动。如果你真的需要QObjects不是你对象的子类,那么在你的worker中重写moveToThread(),这样它也会移动所有必要的东西。
发布于 2014-04-27 16:39:15
我不想贬低@sergey-tachenov的优秀答案,但在Qt5中,你可以停止使用信号和槽,简化你的代码,并具有编译时检查的优势:
void ChildProcesses::start()
{
QThread *childrenWatcherThread = new QThread();
ChildrenWatcher *childrenWatcher = new ChildrenWatcher();
childrenWatcher->moveToThread(childrenWatcherThread);
// These three signals carry the "outcome" of the worker job.
connect(childrenWatcher, ChildrenWatcher::exited,
ChildProcesses::onChildExited);
connect(childrenWatcher, ChildrenWatcher::signalled,
ChildProcesses::onChildSignalled);
connect(childrenWatcher, ChildrenWatcher::stateChanged,
ChildProcesses::onChildStateChanged);
// Make the watcher watch when the thread starts:
connect(childrenWatcherThread, QThread::started,
childrenWatcher, ChildrenWatcher::watch);
// Make the watcher set its 'stop' flag when we're done.
// This is performed while the watch() method is still running,
// so we need to execute it concurrently from this thread,
// hence the Qt::DirectConnection. The stop() method is thread-safe
// (uses a mutex to set the flag).
connect(this, ChildProcesses::stopped,
childrenWatcher, ChildrenWatcher::stop, Qt::DirectConnection);
// Make the thread quit when the watcher self-destructs:
connect(childrenWatcher, ChildrenWatcher::destroyed,
childrenWatcherThread, QThread::quit);
// Make the thread self-destruct when it finishes,
// or rather, make the main thread delete it:
connect(childrenWatcherThread, QThread::finished,
childrenWatcherThread, QThread::deleteLater);
childrenWatcherThread->start();
}
https://stackoverflow.com/questions/4093159
复制相似问题