Qt:信号/槽设计和性能

3

我最近开始使用Qt,并且需要对信号/槽机制进行一些澄清。我理解它是GUI和生活在不同线程的对象之间通信的好工具,但我不确定在像下面这样简单的情况下是否应该使用它。

我有三个类,让我们称它们为MainWindow、DataManager和DataWorker。DataWorker位于一个单独的线程中,当有新数据可以收集时发出信号。经过一些处理后,它会在MainWindow小部件中显示。我创建了DataManager类,以避免将处理代码污染到GUI类中。

现在,我应该如何处理DataManager和MainWindow之间的通信。


选项#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
    }
}

选项#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
    }
}

对我来说,选项1似乎更直观,但是我不太熟悉Qt。如果有更多的类需要响应newData()信号,使用信号和槽的好处就能显现出来。

那么哪个选项更好,并且这两者之间是否有性能差异?

3个回答

3
第一个选项提供的性能比使用信号/槽机制更好,但它也有缺点,即MainWindowDataManager之间存在紧密耦合。它们互相了解,因此不能真正分开使用。这就是重构代码的原因。
话虽如此,作为第三个选项,您可以让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(...);
};

3
您不能直接从您的线程调用GUI函数。因此,选项#1不起作用,除非DataWorker和DataManager :: eventNewData()之间存在信号/槽连接。从另一个线程调用GUI函数的唯一方法是通过信号/槽连接。
信号/槽通信总是会产生开销,因为它完全基于运行时并且操作字符串(插槽的名称在编译时生成,然后在运行时进行比较)。这是动态分派。直接函数调用总是更快的。
所以答案很简单:如果您不需要信号,请不要使用它。在这种特殊情况下,看起来您不需要在DataManager和MainWindow之间进行通信,因为您想要的只是调用MainWindow :: displayData()。看起来您永远不需要动态更改它并调用不同的函数。信号和槽的作用是提供线程间通信和交换机式连接。如果两者都不需要,则没有必要使用信号。

这些对象在同一个线程中,只有DataWorker不在,但这并不是我们关心的问题。 - jaho
@Marian 当DataWorker直接调用DataManager成员函数时,该函数在该DataWorker的线程中执行。 DataManager对象所在的线程并不重要。唯一重要的是调用函数的线程,因为函数总是在调用者的线程中执行。 - Nikos C.
它的实现方式是DataWorker发出一个信号,触发DataManager中的一个槽。那个槽在DataWorker的线程中执行吗? - jaho
不,它在DataManager的线程中执行。但我认为选项#1不使用信号。 - Nikos C.
DataWorker - DataManager的通信使用信号,但我想问的是DataManager - MainWindow之间的通信。这是否意味着第一种选项更好?如果是这样,是否存在在同一线程中实现对象间信号和槽通信的情况? - jaho
是的。看起来你不需要使用插槽,因此没有理由使用它。直接函数调用总是比发射信号更快。 - Nikos C.

0

我不确定QT的工作原理,所以不确定是否会有性能损失,但我的直觉是: 1. 不应该有太大差异 2. 在GUI上下文中,这可能并不重要。

使用标准QT机制的选项具有DataManager不需要了解GUI的好处。许多人认为这是良好的设计,因为它可以允许您替换GUI而不更改内部内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接