connect函数的第五个参数

connect第五个参数介绍

  1. 每一个线程都有自己的事件队列与事件循环系统(exec())。
  2. 线程通过事件队列接收信号
  3. 信号在事件循环中被处理

Qt:: Connection

自动连接:默认的方式。信号发出的线程和糟的对象在一个线程的时候相当于:DirectConnection, 如果是在不同线程,则相当于QueuedConnection。

Qt::DirectConnection(是同步)

直接连接:相当于直接调用槽函数,但是当信号发出的线程和槽的对象在一个线程的时候,信号发出后,对应的槽函数将马上被调用。emit语句后的代码将在全部槽函数运行完成后被运行。在这个线程内是顺序运行、同步的。
如果信号所在线程和槽函数所在线程不是一个线程,会强制把槽函数拉到和信号所在的一样的线程来执行,并且是同步执行,这时打印的槽函数线程和信号线程ID是一个ID(为信号发送时所在线程的ID),emit后面的内容需要等到槽函数执行完毕才执行。

  代码大意:创造其他线程发送信号,触发出线程中MyObject对象中的槽函数,打印线程号,观察执行时机、执行线程号的差异。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

//MyObject.h
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = 0);

signals:

protected slots:
void testSlot();
};


//MyObject.cpp
void MyObject::testSlot()
{
qDebug() << "void MyObject::testSlot() tid = " << QThread::currentThreadId();
}


//MyThread.h
class TestThread : public QThread
{
Q_OBJECT

protected:
void run();
public:
explicit TestThread(QObject *parent = 0);

signals:
void testSignal();

};

//MyThread.cpp
void TestThread::run()
{
qDebug() << "void TestThread::run() -- begin tid = " << currentThreadId();

for(int i=0; i<3; i++)
{
qDebug() << "void TestThread::run() i = " << i;

sleep(1);
}

emit testSignal();

exec();

qDebug() << "void TestThread::run() -- end";
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

//main.cpp
void direct_connection()
{
static TestThread t;
static MyObject m;

QObject::connect(&t, SIGNAL(testSignal()), &m, SLOT(testSlot()), Qt::DirectConnection);

t.start();
t.wait(5 * 1000);
t.quit();
}

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

qDebug() << "main() tid = " << QThread::currentThreadId();

direct_connection();

return a.exec();
}


执行结果:

1
2
3
4
5
6
7
8
9
10

//输出结果:
main() tid = 0x2ff4
void TestThread::run() – begin tid = 0x1d98 //emit线程
void TestThread::run() i = 0
void TestThread::run() i = 1
void TestThread::run() i = 2
void MyObject::testSlot() tid = 0x1d98 // 在emit线程中同步执行槽函数
void TestThread::run() – end//执行完了emit线程继续执行

Qt::QueuedConnection(排队方式,是异步)

无论信号从哪个线程发出,信号被发送至接收对象所在线程的信号队列中,需等到接收对象所属线程的事件循环取得控制权(处理完所有事件处于空闲)时才取出该信号并调用对应的槽函数。发送信号的线程中emit语句后的代码将在发出信号后马上被运行,无需等待槽函数运行完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//只改变main.cpp
void queued_connection()
{
static TestThread t;
static MyObject m;

QObject::connect(&t, SIGNAL(testSignal()), &m, SLOT(testSlot()), Qt::QueuedConnection);

t.start();

t.wait(5 * 1000);

t.quit();
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

qDebug() << "main() tid = " << QThread::currentThreadId();

queued_connection();

return a.exec();
}


1
2
3
4
5
6
7
8
9
10

//输出结果:
main() tid = 0x1fbc//接收者所在线程
void TestThread::run() – begin tid = 0x32f0 //emit线程
void TestThread::run() i = 0
void TestThread::run() i = 1
void TestThread::run() i = 2 //此时emit发送
void TestThread::run() – end //emit线程继续执行
void MyObject::testSlot() tid = 0x1fbc //接收者线程执行槽函数

注意Qt::QueuedConnection与Qt::DirectConnection

  1. 在同线程中使用时,如果信号触发的槽函数中包括exec()函数,需注意:exec()函数相当于打开事件循环,会阻塞掉该槽函数所在的connect()函数,即无法接收到信号。
  2. Qt::QueuedConnection的异步体现在发送信号线程中发送完毕会马上执行接下来的代码,而信号发送到接收者所在线程中也是要信号排队等候执行的(同步)。
  3. exec()不会阻塞和自己同级的connect(),但会阻塞自己所在的connect()。

Qt::BlockingQueuedConnection (信号和槽必须在不同的线程中。否则就产生死锁)

阻塞连接:当前线程发出信号后,当前线程emit后的程序会阻塞,等待其他线程的槽函数执行完毕后才继续执行,相当于是不同的线程能够同步起来运行。

Qt::UniqueConnection

防止重复连接。如果当前信号和槽已经连接过了,就不再连接了。同步异步机制与默认方式一样。