g19*_*tic 62 qt multithreading qt4 qthread
QThread的Qt文档说是从QThread创建一个类,并实现run方法.
以下内容来自4.7 Qthread文档......
要创建自己的线程,请继承QThread并重新实现run().例如:
class MyThread : public QThread
{
public:
void run();
};
void MyThread::run()
{
QTcpSocket socket;
// connect QTcpSocket's signals somewhere meaningful
...
socket.connectToHost(hostName, portNumber);
exec();
}
Run Code Online (Sandbox Code Playgroud)
所以在我创建的每一个线程中,我都做到了这一点,并且对于大多数事情来说它工作正常(我没有在我的任何对象中实现moveToThread(this)并且它工作得很好).
我上周遇到了障碍(设法通过解决我创建对象的地方来解决它)并找到了以下博文.这里基本上说子类化QThread确实不是正确的方法(并且文档不正确).
这是来自Qt的开发人员,所以乍一看我很感兴趣,经过进一步的反思,同意他.按照OO原则,你真的只想子类化一个类来进一步增强该类......不要直接使用类方法......这就是为什么要实例化...
让我们说我想将一个自定义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 &);
};
Run Code Online (Sandbox Code Playgroud)
我仍然认为值得指出的是,它们包含一个Worker::workerThread不必要的额外成员,并且从未在他们的示例中使用过.删除该部分,它是如何在Qt中进行线程的正确示例.
Arn*_*nce 31
关于我唯一想要添加的是进一步说明QObjects与单个线程有亲缘关系.这通常是创建的线程QObject.因此,如果您QObject在应用程序的主线程中创建了一个并希望在另一个线程中使用它,则需要使用它moveToThread()来更改关联.
这节省了必须QThread在run()方法中子类化和创建对象,从而保持您的东西很好地封装.
该博客帖子确实包含一个示例链接.它很短,但它显示了基本的想法.创建你的QObjects,连接你的信号,创建你的QThread,移动你QObjects的QThread并启动线程.信号/插槽机制将确保正确且安全地交叉线程边界.
如果必须在该机制之外调用对象上的方法,则可能必须引入同步.
我知道Qt 除了线程之外还有一些其他不错的线程设置可能值得熟悉但我还没有这样做:)
以下是如何正确使用QThread的一个示例,但它有一些问题,这些问题反映在注释中.特别是,由于没有严格定义执行槽的顺序,因此可能导致各种问题.2013年8月6日发布的评论很好地解决了如何处理这个问题.我在我的程序中使用类似的东西,这里有一些示例代码来澄清.
基本思路是一样的:我创建一个QThread实例,它存在于我的主线程中,一个工作者类实例,它存在于我创建的新线程中,然后我连接所有信号.
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();
}
Run Code Online (Sandbox Code Playgroud)
一些背景:
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);
};
Run Code Online (Sandbox Code Playgroud)
这是怎么回事.当所有这些东西都启动时,调用ChildProcess :: start()方法(见上文).它创建了一个新的QThread和一个新的ChildrenWatcher,然后将其移动到新的线程.然后我连接三个信号,通知我的经理关于其子进程的命运(退出/发信号/上帝知道发生了什么).然后开始主要乐趣.
我将QThread :: started()连接到ChildrenWatcher :: watch()方法,以便在线程准备好后立即启动它.由于观察者生活在新线程中,因此执行watch()方法(排队连接用于调用插槽).
然后我使用Qt :: DirectConnection将ChildProcesses :: stopped()信号连接到ChildrenWatcher :: stop()槽,因为我需要异步执行它.这是必需的,所以我的线程在不再需要ChildProcesses管理器时停止.stop()方法如下所示:
void ChildrenWatcher::stop()
{
mutex.lock();
stopped = true;
mutex.unlock();
}
Run Code Online (Sandbox Code Playgroud)
然后是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();
}
Run Code Online (Sandbox Code Playgroud)
哦,isStopped()方法只是在while()条件下使用互斥的一种方便的方法:
bool ChildrenWatcher::isStopped()
{
bool stopped;
mutex.lock();
stopped = this->stopped;
mutex.unlock();
return stopped;
}
Run Code Online (Sandbox Code Playgroud)
所以这里发生的是我在需要完成时设置停止标志,然后下次调用isStopped()它返回false并且线程结束.
那么watch()循环结束时会发生什么?它调用deleteLater(),因此只要控件返回到线程事件循环,对象就会自行破坏,这发生在deleteLater()调用之后(当watch()返回时).回到ChildProcesses :: start(),您可以看到从观察者的destroyed()信号到线程的quit()槽的连接.这意味着当观察者完成时线程自动完成.当它完成时,它也会自毁,因为它自己的finished()信号连接到它的deleteLater()槽.
这与Maya发布的几乎相同,但因为我使用了自毁语法,所以我不需要依赖于调用插槽的顺序.它总是先自我毁灭,稍后停止线程,然后它自我毁灭.我可以在worker中定义一个finished()信号,然后将它连接到自己的deleteLater(),但这只会意味着一个连接更多.因为我不需要为任何其他目的使用finished()信号,所以我选择从worker本身调用deleteLater().
Maya还提到您不应该在worker的构造函数中分配新的QObject,因为它们不会存在于您将worker移动到的线程中.无论如何我都会这么做,因为这就是OOP的工作方式.只要确保所有这些QObject都是worker的子节点(即使用QObject(QObject*)构造函数) - moveToThread()将所有子节点与正在移动的对象一起移动.如果你真的需要让QObject不是你对象的子对象,那么在你的worker中覆盖moveToThread(),这样它就会移动所有必要的东西.
| 归档时间: |
|
| 查看次数: |
69538 次 |
| 最近记录: |