没有连接的调用槽方法?
我有一个以下列方式实现的活动对象。它用于在后台执行长时间的任务。主线程通过向公共时隙发送信号来调用任务(即,doTask)。这是一个精简的例子(未经测试)。没有连接的调用槽方法?
class MyTask : public QObject
{
Q_OBJECT
public:
MyTask();
~MyTask();
public slots:
void doTask(int param);
private slots:
void stated();
signals:
void taskCompleted(int result);
private:
QThread m_thread;
};
MyTask::MyTask()
{
moveToThread(&m_thread);
connect(&m_thread, SIGNAL(started()), this, SLOT(started()));
m_thread.start();
}
MyTask::~MyTask()
{
// Gracefull thread termination (queued in exec loop)
if(m_thread.isRunning())
{
m_thread.quit();
m_thread.wait();
}
}
void MyTask::started()
{
// initialize live object
}
void MyTask::doTask(int param)
{
sleep(10);
emit taskCompleted(param*2);
}
只要信号调用doTask(),该(应该)按预期工作。但是,如果主线程直接调用doTask(),它将由主线程执行。对于某些任务,即使插槽方法是直接调用的,我也希望通过活动对象的线程强制执行。
我可以在doTask()前添加代码来检查当前线程是否是m_thread,在这种情况下它会执行该方法。如果不是,我希望doTask()向'this'发出一个信号,以便调用doTask()在m_thread exec循环中排队并尽快执行。
我该怎么做?
编辑:基于建议的答案,这里是新的代码。即使直接由主线程调用,doTask方法现在也会委托活动objet的线程执行。通过信号调用仍然按预期工作。
class MyTask : public QObject
{
Q_OBJECT
public:
explicit MyTask(QObject *parent = 0);
~MyTask();
public slots:
void doTask(int param);
private slots:
void doTaskImpl(int param);
signals:
void taskCompleted(int result);
private:
QThread m_thread;
};
MyTask::MyTask(QObject *parent) : QObject(parent)
{
moveToThread(&m_thread);
m_thread.start();
}
MyTask::~MyTask()
{
// Gracefull thread termination (queued in exec loop)
if(m_thread.isRunning())
{
m_thread.quit();
m_thread.wait();
}
}
void MyTask::doTask(int param)
{
QMetaObject::invokeMethod(this, "doTaskImpl", Q_ARG(int, param));
}
void MyTask::doTaskImpl(int param)
{
// Do the live oject's asynchronous task
sleep(10);
emit taskCompleted(param*2);
}
这是我能找到的最简单的实现来支持在单独的线程中执行异步方法。只要线程启动,doTask()方法的调用就会排队并处理。当从对象线程调用时,它将立即执行(不排队)。
请注意,只有在启动线程时才会发出启动()信号。这意味着在启动线程之前排队的doTask()方法调用将在启动()方法插槽被调用之前执行。这是我从初始实施中删除它的原因。因此,对象初始化应该最好在构造函数中执行。
您想致电QMetaObject::invokeMethod
来做到这一点。在你的情况下,它会看起来像
MyTask *task;
int param;
// ...
// Will automatically change threads, if needed, to execute
// the equivalent of:
// (void)task->doTask(param);
QMetaObject::invokeMethod(task, "doTask", Q_ARG(int, param));
这真棒。它像一个魅力。 看我编辑使用的代码。 – chmike 2010-07-22 09:11:08
有没有可能做到这一点,而不使用字符串名称的方法? – 2017-01-27 15:59:37
关于我想补充的唯一的改进是为了节省一些时间查找的方法:
class MyTask {
// ...
private:
int m_doTaskImplIndex;
};
MyTask::MyTask() :
//...
m_doTaskImplIndex(metaObject()->indexOfMethod("doTaskImpl"))
//...
{}
void MyTask::doTask(int param)
{
metaObject()->method(m_doTaskImplIndex).invoke(this, Q_ARG(int, param));
}
通过这项额外的努力,您获得多少收益? – 2012-05-29 13:48:09
传递给'indexOfMethod'的方法的签名应该包含参数并且处于规范化形式([link](https://qt-project.org/doc/qt-5.0/qtcore/qmetaobject.html#indexOfMethod))正确的代码应该是'm_doTaskImplIndex(metaObject() - > indexOfMethod(metaObject() - > normalizedSignature(“doTaskImpl(int)”)))'' – 2013-08-17 16:22:49
那么,如何包装这一切到一个不错的课程?
我还添加了一个插槽finishPlease
,它将作为消息待办事项列表中的最后一个元素添加,并在主程序实际处理完所有未决消息并将其终止之前向主程序提供反馈。
class Threaded : public QObject
{
Q_OBJECT
public:
Threaded() {
thread = new QThread(this);
this->moveToThread(thread);
connect(thread, SIGNAL(started()), this, SLOT(init()), \
Qt::QueuedConnection);
thread->start();
}
virtual ~Threaded() {
thread->exit();
thread->wait();
delete thread;
}
signals:
void okayKillMe();
public slots:
virtual void init() = 0;
void finishPlease() {emit okayKillMe();}
protected:
QThread* thread;
};
class MyClass : public Threaded
{
Q_OBJECT
public:
MyClass() { }
virtual ~MyClass() { }
public slots:
void init() { }
void doStuff() { }
void doOtherStuff(int* data) { }
};
我怀疑MyTask中有一个bug。该文件说,你不能移动的物体,如果另一个线程:如果我理解Qt的内部,如果parent
不是0
我看到这里有一个问题正确,则
moveToThread(&m_thread);
将失败有一位家长。如果MyTask是由父级创建的,您的代码是否工作? – andref 2010-07-28 14:18:19
一个类似的问题也已经在[QT +如何从不同线程中运行的自定义C++代码中调用插槽](http:// stackoverflow。COM /问题/ 1144240/QT-如何拨号槽 - 从定制-C代码运行-IN-A-不同的线程)。 – Trilarion 2014-05-23 09:35:05