Qt中的slot函数是否在另一个线程上运行?

sta*_*mer 4 c++ qt qtnetwork qt-signals qtcore

在以下函数中,管理器将发出finished(QNetworkReply*)信号,然后getCategories(QNetworkReply*)将调用插槽函数.

    void getCategories()
    {
        connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(getCategories(QNetworkReply*)));

        for(int i = 0; i < stores.size(); ++i)
        {
            request.setUrl(QUrl(QString("http://www.example.com/%1").arg(stores[i].store_id)));

            manager.get(request);
        }
    }
Run Code Online (Sandbox Code Playgroud)

如果在第一次调用slot函数时发出第二个信号,Qt是否会启动另一个线程来运行slot函数作为对第二个信号的响应?如果是这样,是否有一些方法让第二次调用slot函数等到第一次调用结束?

更新:

我的意思是插槽功能有可能同时运行吗?

Fra*_*eld 5

除非你明确地创建它们,否则Qt程序中没有"可见的"多线程.这意味着,Qt可能会在内部使用线程,例如网络I/O,但多线程会被屏蔽,您不必关心它.您的所有代码都不会从其他线程调用,也不会通过需要您关心同步的方式与其他线程共享您的数据.

信号/插槽连接有两种主要形式:直接连接和排队连接:

直接连接只是同步执行的函数调用,其间有一些额外的函数调用和查找表.如果发出信号,则在"发出信号();"之前立即调用所有插槽.返回(正如Laszlo的例子所示).

另一方面,排队连接也在主线程上执行,没有任何并行/多线程.然而,狭槽呼叫排队在事件循环的事件队列:做发射方法中,首先完成时,控制返回到事件循环,然后在某个时间点以后调用槽(S).这些插槽是一个接一个地调用的 - 执行的顺序可能没有定义,但它们永远不会被并行调用.

现在QNetworkAccessManager(QNAM)也可以使用事件驱动(*),排队事件:你调用get(),控件返回事件循环; QNAM发送请求; 之后,在执行网络I/O后,QNAM会收到响应.将网络I/O和响应完成视为事件,例如鼠标点击:事件发生,它被放入事件队列,之后事件循环调用QNAM中的一些内部插槽,然后触发finish()被发射并调用mySlot().

(*)实际上,根据平台,QNAM可能确实使用多线程.但这是一个隐藏在用户身上的实现细节 - 因此人们可以坚持使用"事件驱动"的心理模型.


lpa*_*app 3

它不会在第二个线程中启动它。AFAIK,默认情况下它将是直接调用或排队等待处理。这种情况还取决于您如何管理线程。

您可以选择多种连接类型,但通常使用默认连接类型(直接或排队)就可以了

我将向您展示两个示例来证明它还取决于到底发出信号的内容。

情况 1(由正在执行的槽发出)

主程序

#include <QObject>
#include <QThread>
#include <QDebug>
#include <QCoreApplication>

class MyClass : public QObject
{
    Q_OBJECT

    public:
        explicit MyClass(QObject *parent = 0) : QObject(parent), counter(0)
        {
            connect(this, SIGNAL(mySignal()),
                    this, SLOT(mySlot()), Qt::QueuedConnection);
        }

    signals:
        void mySignal();
    public slots:
        void mySlot()
        {
            if (counter >= 2) return;
            ++counter;
            qDebug() << "mySlot started";
            emit mySignal();
            QThread::msleep(1000);
            qDebug() << "mySlot quit";
        }

    private:
        int counter;

};

#include "main.moc"

int main(int argc, char **argv)
{
    QCoreApplication application(argc, argv);
    MyClass myObject;
    myObject.mySlot();
    return application.exec();
}
Run Code Online (Sandbox Code Playgroud)

主程序

TEMPLATE = app
TARGET = test
QT = core
SOURCES += main.cpp
Run Code Online (Sandbox Code Playgroud)

构建并运行

moc -o main.moc main.cpp && qmake && make && ./test
Run Code Online (Sandbox Code Playgroud)

输出

mySlot started 
mySlot quit 
mySlot started 
mySlot quit
Run Code Online (Sandbox Code Playgroud)

在我的示例中,您将获得以下没有排队连接的输出,表明这将是槽执行过程中的直接调用:

mySlot started
mySlot started
mySlot quit  
mySlot quit
Run Code Online (Sandbox Code Playgroud)

情况 2(不是由正在执行的槽发出)

主程序

#include <QObject>
#include <QThread>
#include <QDebug>
#include <QCoreApplication>
#include <QTimer>

class MyClass : public QObject
{
    Q_OBJECT

    public:
        explicit MyClass(QObject *parent = 0) : QObject(parent), counter(0), timer(new QTimer(this))
        {
            // Note: there is no need for queued connection in this case
            connect(this, SIGNAL(mySignal()), this, SLOT(mySlot()));
            connect(timer, SIGNAL(timeout()), this, SLOT(mySlot()));
            timer->setSingleShot(true);
            timer->start(200);
        }

    signals:
        void mySignal();
    public slots:
        void mySlot()
        {
            ++counter;
            qDebug() << "mySlot started" << counter;
            QThread::msleep(1000);
            qDebug() << "mySlot quit" << counter;
        }

    private:
        int counter;
        QTimer *timer;

};

#include "main.moc"

int main(int argc, char **argv)
{
    QCoreApplication application(argc, argv);
    MyClass myObject;
    myObject.mySlot();
    return application.exec();
}
Run Code Online (Sandbox Code Playgroud)

主程序

TEMPLATE = app
TARGET = test
QT = core
SOURCES += main.cpp
Run Code Online (Sandbox Code Playgroud)

构建并运行

moc -o main.moc main.cpp && qmake && make && ./test
Run Code Online (Sandbox Code Playgroud)

输出

mySlot started 1 
mySlot quit 1 
mySlot started 2 
mySlot quit 2
Run Code Online (Sandbox Code Playgroud)