在Qt世界中,我们可以把繁重的任务放在分离的类上,并使用信号/插槽机制进行函数调用,然后,如果适当的设计和实现将导致在一个分离的线程上调用。
然而,在我看来,有了所有这些,人们仍然可以直接(不恰当地)进行函数调用。这将导致在同一个线程上调用。
所以我想知道是否有关于如何编写接口来使用信号/插槽机制强制调用的想法?或者,我们所能做的就是抱最好的希望。
您可以创建一个瘦包装器对象,它将直接调用代理到发出信号中,这些信号触发单独线程中实际工作线程的调用槽。下面是一个简单的例子:
class Task : public QObject {
Q_OBJECT
public:
void doStuff();
};
class TaskRunner {
Task* m_task;
QThread* m_thread;
public:
TaskRunner() {
m_thread = new QThread;
m_task = new Task;
connect(this, &TaskRunner::doStuffRequest, m_task, &Task::doStuff);
m_task->moveToThread(m_thread);
m_thread->start();
}
void doStuff() {
emit doStuffRequest();
}
signals:
void doStuffRequest();
}
在Task
中可以将Task
对象的构造函数设为private,将TaskRunner
设为friend,这样用户就不能直接实例化Task
对象。
对于一个现实世界的例子,我可能会让一个跑步者QObject
-继承。另外,不要忘记在析构函数中正确地停止线程并清理内存。
一旦将slot(方法)设为公共,就不能阻止它被直接调用。因此,唯一的解决方案是将插槽设为私有。但是,您只能从类内部建立连接。因此,你需要传递发出类的实例,例如在构造函数中。
class SomeReceiver: public QObject
{
Q_OBJECT
public:
explicit SomeReceiver(SomeEmitter *emitter, QObject *parent = nullptr) : QObject(parent)
{
connect(emitter, &SomeEmitter::someSignal, this, &SomeReceiver::someSlot);
}
private:
void someSlot() // you cannot call it directly from outside, it is private
{
// some stuff here ...
}
}
(或者,不是在构造函数中建立连接,你可以创建一个setter函数,它将接受一个指向发射器实例的指针并建立连接。但是这个setter有被调用两次的危险。所以我更喜欢使用构造函数…)
你可以这样使用:
auto emitter = new SomeEmitter();
auto receiver = new SomeReceiver(emitter);
上面的代码确保通过信号和插槽调用。
如果你想让代码在二级线程中执行,那么只需将接收器移动到该线程中。
receiver->moveToThread(&someThread);
…然后每次发出发射器的信号时,连接的槽将始终在线程中执行。如果你不把它移到次线程,它将在主/GUI线程中执行。
要强制将这个对象移动到一个线程中,那么你也可以在构造函数中移动它(通过传递一个线程指针给构造函数,或者通过在构造函数中创建一个线程——但是你需要注意所有的线程解构)。我强烈不建议这样做,这是一个非常丑陋的设计。
PS:如果你真的想强制一些代码在线程中运行,那么你应该继承QThread
并覆盖它的run()
方法。这总是在线程中执行。