我希望使用QThread构建我的Qt应用程序,因为我想将长进程表单MainWindow与线程分开。
我在此代码中遇到了问题:
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const std::string ¶m2, std::string ¶m2) {
int result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const int &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::input,&Controller::output, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const int &);
private:
string input;
string output;
};
我希望我的代码可以将两个值(输入,输出)字符串传输到QThread,并且结果将信号作为int值发出。但是,列出的代码不起作用。我在.Net中寻找类似blackgroundWorker的解决方案并具有类似的功能。
我应该这样做吗?
修改
我无法编译这个。我收到了错误:
Severity Code Description Project File Line Suppression State
Error C2664 'QMetaObject::Connection QObject::connect(const QObject *,const char *,const char *,Qt::ConnectionType) const': cannot convert argument 2 from 'std::string Controller::* ' to 'const char *' GPCTool C:\repo\Tool\Controller.cpp 21
答案 0 :(得分:0)
首先,这行代码不起作用:
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
workerThread
存在于主线程中,而worker
存在于由workerThread
处理的线程中。因此,当QThread::finished
被触发时,QObject::deleteLater
将被异步调用(参见Qt::AutoConnection
),但是线程已经完成,异步调用永远不会完成。
甚至变得更糟。即使调用QObject::deleteLater
,也不会发生延迟删除,原因相同:线程已经完成。
connect(this, &Controller::input, &Controller::output, worker, &Worker::doWork);
此调用与connect()
的任何原型都不匹配。您无法将变量连接到插槽。
创建一个中间信号:
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::callDoWork, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
emit callDoWork(input, output);
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
signals:
emit callDoWork(std::string, str::string);
public slots:
void handleResults(const int &);
private:
string input;
string output;
};
或使用QMetaObject::invokeMethod()
:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
QMetaObject::invokeMethod(worker, "doWork", Qt::QueuedConnection, Q_ARG(std::string, input), Q_ARG(std::string, output));
}
请注意,要使这2个解决方案正常工作,您需要使用Q_DECLARE_METATYPE和qRegisterMetaType注册std::string
(请参阅http://doc.qt.io/qt-5/qmetatype.html#Q_DECLARE_METATYPE)。
并且您还希望将std:string &output
替换为std::string *output
,因为Qt会在两个主题之间复制output
,您将看不到更改反映到原始output
变量。
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
由于resultReady
的签名与handleResults
不匹配,因此无法编译。要将信号连接到插槽,它们必须采用相同的参数。
我建议你阅读Qt Signal & Slots documentation
关于你对线程的使用,我认为你应该看看QThreadPool和QRunnable。
答案 1 :(得分:0)
你可能会读到一个愚蠢的博客,声称必须创建一个工作者对象并将其与普通线程一起使用,而不是简单地从QThread
派生并实现run()
。
由于您的工作量只需要一次只需要一次,所以更容易做到这一点。
在任何一种情况下,只需通过构造函数或setter方法将值传递给类,实际上不需要将它们作为参数传递给doWork()