简体   繁体   English

Qt:信号/插槽设计和性能

[英]Qt: signal/slot design and performance

I've recently started using Qt and I need some clarification on signal/slot mechanism. 我最近开始使用Qt,我需要对信号/插槽机制做一些澄清。 I understand how it's a great tool for the GUI and communication between objects living in separate threads, but I'm not quite sure whether I should use it in a simple cases like the following one. 我理解它是一个很好的工具,用于生成在不同线程中的对象之间的GUI和通信,但我不确定是否应该在一个简单的情况下使用它,如下所示。

I have three classes, let's call them MainWindow, DataManager and DataWorker. 我有三个类,我们称之为MainWindow,DataManager和DataWorker。 DataWorker lives in a separate thread and signals when new data is ready for collection. DataWorker位于一个单独的线程中,并在新数据准备好收集时发出信号。 It is then visualised in MainWindow widgets after some processing. 然后在一些处理后在MainWindow小部件中可视化。 I've created DataManager class as to not pollute the GUI class with processing code. 我创建了DataManager类,以便不使用处理代码污染GUI类。

Now, how should I handle the communication between DataManager and the MainWindow. 现在,我应该如何处理DataManager和MainWindow之间的通信。


Option #1 - have a pointer to MainWindow as a member and just call its method 选项#1 - 将指向MainWindow的指针作为成员,并调用其方法

class MainWindow
{
private:
    DataManager* dm;

public:
    MainWindow() : dm(new DataManager(this)) { }
    displayData(const char* processedData);
}

class DataManager : QObject
{
private:
    MainWindow *mw;

private slots;
    eventNewData()
    {
        // get and process the data
        mw = this->QObject.parent();
        mw->displayData(const char* processedData);
        // release data
    }
}

Option #2 - signal new data to call MainWindow slot 选项#2 - 发信号通知新数据调用MainWindow插槽

class MainWindow
{
private:
    DataManager* dm;

private slots:
    displayData(const char* processedData);

public:
    MainWindow() : dm(new DataManager(this)) { QObject::connect(dm, SIGNAL(newData(const char*)), this, SLOT(displayData(const char*)); }

};

class DataManager : QObject
{    
signals:
    newData(const char* processedData);

private slots;
    eventNewData()
    {
        // get and process the data
        emit newData(processedData);
        // release data
    }
}

Option 1 seems more intuitive to me, but then again I don't know Qt very well. 选项1对我来说似乎更直观,但后来我再也不太了解Qt。 I can see the benefit of using signals and slots if there will be more classes that I'd like to react to a newData() signal. 如果有更多类我想对newData()信号做出反应,我可以看到使用信号和插槽的好处。

So what's a better option and is there any difference in performance between the two? 那么什么是更好的选择,两者之间的表现有什么不同?

You cannot call GUI functions directly from your thread. 您无法直接从线程调用GUI函数。 So option #1 won't work, unless there's a signal/slot connection between DataWorker and DataManager::eventNewData(). 因此,除非DataWorker和DataManager :: eventNewData()之间存在信号/插槽连接,否则选项#1将不起作用。 The only way to call GUI functions from another thread is through a signal/slot connection. 从另一个线程调用GUI函数的唯一方法是通过信号/插槽连接。

Signal/slot communication always incurs overhead, since it's purely runtime-based and operates on strings (the names of the slots are generated at compile time, and then compared at runtime.) It's dynamic dispatch. 信号/插槽通信总是会产生开销,因为它纯粹是基于运行时的,并且对字符串进行操作(插槽的名称在编译时生成,然后在运行时进行比较。)它是动态调度。 Direct function calls are always faster. 直接函数调用总是更快。

So the answer is simple: if you don't need a signal, don't use one. 所以答案很简单:如果你不需要信号,请不要使用。 In this particular case, it doesn't look like you need it for communication between DataManager and MainWindow, since all you want is call MainWindow::displayData(). 在这种特殊情况下,看起来你不需要它来进行DataManager和MainWindow之间的通信,因为你想要的只是调用MainWindow :: displayData()。 It doesn't look like you would ever need to change that dynamically and call a different function. 看起来你不需要动态地改变它并调用不同的函数。 Signals and slots are there to provide inter-thread communication and switchboard-like connections. 信号和插槽用于提供线程间通信和类似交换机的连接。 If you don't need either, then there's no need to use signals. 如果您不需要,则无需使用信号。

The first option does provide better performance than using the signal/slot mechanism, but it also has the drawback that there is a tight coupling between MainWindow and DataManager . 第一个选项确实提供了比使用信号/插槽机制更好的性能,但它的缺点是MainWindowDataManager之间存在紧密耦合。 Both know each other, so they can't really be used separately. 两者都互相认识,所以不能真正单独使用。 That alone should already be a reason to refactor the code. 仅此一点应该已经成为重构代码的理由。

That said, as a third option you could let MainWindow have the slot that received the signal from DataWorker and let DataManager be a helper class that just converts the data into a usable format for MainWindow . 也就是说,作为第三种选择,你可以让MainWindow拥有从DataWorker接收信号的插槽,让DataManager成为一个帮助类,只需将数据转换为MainWindow的可用格式。

class MainWindow
{
private:
    DataManager* dm;

public:
    MainWindow() : dm(new DataManager()) { }
    displayData(QString processedData);
private slots;
    eventNewData()
    {
        // get the data
        QString processedData = dm->preprocessData(data);
        displayData(processedData);
        // release data
    }
};

class DataManager
{
public:
    QString preprocessData(...);
};

I don't know exactly how QT works so I'm not sure if there is a performance hit but my intuition says 1. there shouldn't be a big difference 2. it will probably not matter anyway in a GUI context. 我不确切知道QT是如何工作的所以我不确定是否有性能损失,但我的直觉说1.应该没有太大的区别2.无论如何在GUI环境中它可能无关紧要。

The option using the standard QT mechanism has the benefit of the DataManager not needing to know anything about the GUI. 使用标准QT机制的选项具有DataManager无需了解GUI的任何好处。 Many people consider this good design because it can allow you to replace your GUI without changes to the internals. 许多人都认为这是一个好的设计,因为它可以让你在不改变内部的情况下更换你的GUI。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM