当再次触发信号从Qt插槽执行的功能再次被调用时,会发生什么情况?

时间:2018-08-04 13:43:38

标签: c++ qt

这是我的情况。

我正在写一个TCP服务器。该服务器将从多个客户端获取请求。

我有一个用于接收信息的类(基于名为MySocket的QTCPSocket),并对其进行缓冲,直到所有信息都到达为止。明确地说,客户端一旦检测到已成功建立与我的服务器类的连接,就会发送其信息。客户端和服务器是在不同计算机(实际上甚至是不同的操作系统)上运行的单独程序。

MySocket收到所有数据后,将向服务器控制类发送qt信号。这被接收到一个插槽中,该插槽根据其唯一参数执行许多操作。当参数告知已接收到数据时,它将调用函数ProcessInformation。该函数需要做一些事情,所以它不是瞬时的。

如果在服务器上收到新的连接请求,该请求将被保存在MySocket的另一个实例中,当接收到所有数据时,它将再次发出其信号。

在这里我不确定会发生什么。我相信Qt Signal-Slot系统将确保这一工作。如果我没记错的话,由于slot函数尚未完成运行,第二个信号将排队,并且我将得到想要的行为:ProcessInformation将在完成第一个信号后第二次被调用跑。但是,我不确定,所以我问:在这种情况下,程序的行为如何?这样我就可以进行编程了。

由于这是在不同计算机上运行的两个不同程序,并且鉴于我需要测试两个同时发送其数据的客户端以查看其效果的事实,所以我不确定如何测试这种情况。

2 个答案:

答案 0 :(得分:4)

  

由于插槽功能尚未完成运行,第二个信号将排队

绝对不是。 Qt从来没有这样做。允许信号和插槽重新进入。

行为取决于有效连接类型

  1. Qt::DirectConnection-连接就像立即通过函数指针进行调用一样。 在信号返回时,插槽已完成执行。实际上,就好像signal()的实现中简单地包含了所有插槽调用一样:

    signal(params) { slot(params); ... }
    
  2. Qt::QueuedConnection-连接的行为就像通过功能指针从exec()中进行调用一样:

    signal(params) { exec.add(slot, params); ... }
    

    然后execQEventLoop::execQCoreApplication::exec)执行以下操作:

    exec() { dispatchEvents(); while (!calls.empty()) calls.take_first().invoke(); }
    

Qt还支持自动连接,这是默认设置,其中有效类型是在运行时在信号内确定的:

signal(params) {
   for (conn: connections)
      sameThread = conn.receiver.thread() == currentThread();
      if (conn.isDirect || (conn.isAutomatic && sameThread))
         conn.slot(params)
      else if (conn.isQueued || (conn.isAutomatic && !sameThread))
         exec.add(conn.slot, params);
}

因此,如果使用默认的自动连接类型,并且调用方和接收方在同一线程中,则调用信号与直接在接收方对象中调用插槽是相同的。如果调用方和接收方位于不同的线程中,则当控件返回事件循环时,调用信号将始终将要执行的调用排队。

请注意,在Qt中,几乎在所有情况下,只要您的代码运行(实际上您的任何代码),exec()都位于调用堆栈中。例如。调用堆栈可能看起来像这样:

MyClass::onButtonClick()    // your slot connected to a button's clicked signal
QAbstractButton::clicked()  // signal - just a C++ method (!)
QPushButton::event()        // the event handler that got the mouse event
QCoreApplication::notify()
... (platform code etc)
QAbstractEventDispatcher::processEvents()
QCoreApplication::exec()
main()
__thread_start() 

       // C runtime code

答案 1 :(得分:0)

好的。因此,这与我所说的完全不同。但是(我认为)已经足够接近了。这是一个只有一个按钮即可测试我的问题的简单Tester小部件的代码。

标题:

#ifndef TESTER_H
#define TESTER_H

#include <QMainWindow>
#include <QTimer>
#include <QElapsedTimer>
#include <QDebug>
#include <QTextStream>
#include <QFile>

namespace Ui {
class Tester;
}

class Tester : public QMainWindow
{
    Q_OBJECT

public:
    explicit Tester(QWidget *parent = 0);
    ~Tester();

public slots:
    void doStuff();

private slots:
    void on_pushButton_clicked();

private:
    Ui::Tester *ui;
    QTimer timer;
    int ntimes;
    void aFunction();
};

#endif // TESTER_H

CPP:

#include "tester.h"
#include "ui_tester.h"

Tester::Tester(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Tester)
{
    ui->setupUi(this);
    ntimes = 50;
    connect(&timer,&QTimer::timeout,this,&Tester::doStuff);
}

void Tester::doStuff(){
    qWarning() << "DO STUFF";
    ntimes--;
    if (ntimes == 0) timer.stop();
    else aFunction();
}

void Tester::aFunction(){

    QFile file("output.txt");
    file.open(QFile::Append);
    QTextStream writer(&file);

    QElapsedTimer measure;
    measure.start();
    qWarning() << "AFUNCTION";
    writer << "CALL: " << ntimes << "\n";
    for (qint32 i = 0; i < 1000000; i++){
        writer << "NUM" << i << "\n";
    }
    file.close();
    qWarning() << "LASTED" << measure.elapsed();

}

Tester::~Tester()
{
    delete ui;
}

void Tester::on_pushButton_clicked()
{
    //aFunction();
    timer.start(50);
}

我的处理功能被简单的aFunction取代了,该功能将一堆数字写入文本文件。我使用了经过的计时器来衡量该功能完成所需的时间,平均大约需要220毫秒。

因此,使用了每50毫秒发送一次信号的计时器来代替“我的所有数据已到达”信号。如果我是正确的,这应该重现我的问题。当功能尚未完成执行时,信号将被发送到具有该功能的插槽。

我测试了一下,因为我们怀疑每次调用都正确地写入了所有数字(我在输出文件上使用grep检查了该数字)。指示如果信号到达尚未完成的插槽,则在上一个调用结束后立即对该插槽的调用进行排队并调用该插槽。

我将不胜感激。