防止直接调用slot,更倾向于连接,这会导致在另一个线程上调用



在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()方法。这总是在线程中执行。

最新更新