QT 다중 스레드 신호 슬롯 메커니즘의 요점

이 문서에서는 다음 내용을 다룹니다.

  1. QT 신호 슬롯 메커니즘
  2. 멀티스레딩에서 qt에 대한 참고 사항
  3. 일부 qt 경험 지식 포인트

 

 

1) 반입 지점 문제

 

이 질문은 언제, 어디서, 누가 QT 신호와 슬롯 기능을 보내고 누가 실행하는지 이해하는 데 중점을 둡니다.

 

이 예를 과소 평가하지 마십시오.저자는 "QT와 함께 5 ~ 6 년 동안 일한"사람을 보았고이 질문을 받았을 때 여전히 "왕 구가 좌우로 말했다"고 알지 못합니다. 대답하는 방법. 이 사람들은 QT를 사용하는 초기 단계에 불과하고 핵심 문제의 문을 건드리지 않은 것으로 간주할 수 있습니다.

 

이 질문에 답하기 전에 몇 가지 기본 지식을 소개해야 합니다.

 

2) 객체가 속한 스레드

 

문제를 설명하기 위해 코드 스니펫을 제공합니다.
 

class MyThread:public QThread

{

MyThread(){p1 = new A()} //p1对象在旧线程

void run(){p2 = new A()}//p2对象在新线程

}

void mian() {

MyThread thread1; //thread1对象在旧线程中

thread1.start();

}

QT 멀티스레딩에서는 QThread::run() 함수만 새 스레드에 있습니다.

 

실행중인 새 개체는 새 스레드에 있습니다.

또한 생성자의 새 개체인 스레드 개체 자체는 여전히 이전 스레드에 있습니다.

MyThread의 생성자는 여전히 메인 스레드에서 호출되므로 p1은 메인 스레드에 있습니다.

 

이러한 점은 매우 중요합니다.

 

3) 교재에서 헷갈리는 qt connect 5번째 매개변수

다섯 번째 매개변수는 슬롯 함수가 실행되는 스레드를 나타냅니다.

1) 기본 연결 방식인 AutoConnection은 시그널과 슬롯, 즉 송신자와 수신자가 동일한 스레드에 있으면 직접 연결에 해당하고 송신자와 수신자가 다른 스레드에 있으면 대기열 연결.

2) 직접 연결(DirectConnection), 시그널이 방출되면 슬롯 함수가 즉시 호출됩니다. 슬롯 함수의 객체가 어느 스레드에 있든 슬롯 함수는 항상 송신자가 위치한 스레드에서 실행됩니다. 즉, 슬롯 함수와 신호 송신자는 동일한 스레드에 있습니다.

3) 큐 연결(QueuedConnection), 수신자가 위치한 쓰레드의 이벤트 루프로 컨트롤이 리턴되면 슬롯 함수가 호출된다. 슬롯 함수는 리시버가 위치한 스레드에서 실행됩니다. 즉, 슬롯 함수와 시그널 리시버는 같은 스레드에 있습니다.

4) 대기열 연결 잠금(QueuedConnection)

Qt::BlockingQueuedConnection: 슬롯 함수의 호출 타이밍은 Qt::QueuedConnection과 동일하지만 송신자가 위치한 스레드는 슬롯 함수 실행이 완료될 때까지 신호를 보낸 후 차단됩니다. 받는 사람과 보낸 사람이 같은 스레드에 있으면 안 됩니다. 그렇지 않으면 프로그램이 교착 상태에 빠집니다. 여러 스레드 간의 동기화가 필요한 경우 필요할 수 있습니다.

5) 단일 연결(QueuedConnection)

Qt::UniqueConnection: 이 플래그는 비트 단위 또는 (|)로 위의 4개와 조합하여 사용할 수 있습니다. 이 플래그가 설정되면 신호와 슬롯이 이미 연결된 경우 반복되는 연결은 실패합니다. 즉, 반복되는 연결을 피하기 위해

참고: 여기에서 "아마도"라는 교과서적인 진술은 모호하고 잘못되었습니다. 나중에 여기에서 실수를 수정할 것입니다.

이 문구를 처음 본다면 "보내는 사람"의 개념이 헷갈리시나요? 또는 여기 설명을 사용하여 처음에 질문에 답할 수 있습니까?

 

4) 예제로 돌아가기

 

처음의 예에서는 새로운 제한을 추가했으며 신호와 슬롯이 직접 연결되었습니다.

 

1) pa는 주 스레드에 속합니다.

2) 직접 연결 사용

 

신호의 발신자는 누구입니까? 메인 스레드입니까, 자식 스레드입니까, 아니면 다른 "무언가"입니까?

 

참고: 이 개념을 모르고 왼쪽에 있는 세 가지 질문에 명확하게 대답할 수 없다면 멀티스레딩에서 신호 슬롯 메커니즘을 완전히 이해하지 못한 것입니다.

 

답변: 신호 발신자는 다른 "사물"이 아닌 스레드이며 자식 스레드입니다. 직접 연결 방식으로 인해 자식 스레드에서 슬롯 함수가 호출됩니다.

언제 호출됩니까? 함수 포인터의 방식과 유사하게, emit이 제출되면 "함수 포인터"를 호출하는 방식과 유사하게 자식 스레드에서 직접 실행됩니다.

 

5) 교과서에 있는 내용 수정

다섯 번째 매개변수는 슬롯 함수가 실행되는 스레드를 나타냅니다.

1) 기본 연결 방식인 자동 연결(AutoConnection)은 시그널과 슬롯, 즉 " 시그널을 보내는 쓰레드"와 "수신자가 위치한 쓰레드"가 같은 쓰레드라면, 이는 a와 동일하다. 직접 연결; "신호를 보내는 스레드"인 경우 큐 연결에 해당하는 "수신자가 위치한 스레드"가 있는 스레드가 아닙니다.

2) 직접 연결(DirectConnection), 시그널이 방출되면 슬롯 함수가 즉시 호출됩니다. 슬롯 함수의 개체가 어떤 스레드에 있든 슬롯 함수는 항상 " 신호를 보내는 스레드 " 에서 실행됩니다 . 즉, 슬롯 함수와 " 신호를 보내는 스레드 " 는 동일한 스레드에 있습니다.

3) 큐 연결(QueuedConnection), 수신자가 위치한 쓰레드의 이벤트 루프로 컨트롤이 리턴되면 슬롯 함수가 호출된다. 슬롯 함수는 수신자가 있는 스레드에서 실행됩니다. 즉, 슬롯 함수는 "시그널 수신자가 있는 스레드"와 동일한 스레드에 있습니다.

4) 대기열 연결 잠금(QueuedConnection)

Qt::BlockingQueuedConnection: 슬롯 함수의 호출 타이밍은 Qt::QueuedConnection과 동일하지만 신호를 보낸 후 " 신호를 보내는 스레드 "는 슬롯 함수 실행이 완료될 때까지 차단됩니다. "수신자가 있는 스레드"와 "신호를 보내는 스레드"는 같은 스레드에 있으면 안 됩니다. 그렇지 않으면 프로그램이 교착 상태가 됩니다. 여러 스레드 간의 동기화가 필요한 경우 필요할 수 있습니다.

5) 단일 연결(QueuedConnection)

Qt::UniqueConnection: 이 플래그는 비트 단위 또는 (|)로 위의 4개와 조합하여 사용할 수 있습니다. 이 플래그가 설정되면 신호와 슬롯이 이미 연결된 경우 반복되는 연결은 실패합니다. 즉, 반복되는 연결을 피하기 위한 것입니다.

 

 

주의점:

 

"시그널을 보내는 스레드"는 코드에서 방출 신호가 호출될 때 실행 스레드를 말하며 "시그널 객체"가 속한 스레드가 아닙니다.

세 가지 요소가 슬롯 함수가 호출되는 스레드를 결정합니다.

 

  1. 신호를 보내기 위해 emit 을 호출한 스레드.
  2. 받는 사람 개체가 상주하는 스레드입니다.
  3. connect의 다섯 번째 매개변수입니다.

 

참고: 슬롯 기능이 실행되는 위치는 연결 기능이 작성될 때(컴파일 시간)가 아니라 동적으로 결정됩니다.

 

6) 슬롯 기능 실행 시

 

각 스레드에는 자체 메시지 이벤트 루프 큐가 있습니다.

직접 연결 방법은 소위 즉시 실행, 즉 함수가 직접 호출됩니다.

대기열 모드는 즉시 실행되지 않으며 단일/다중 스레드 상황으로 나뉩니다.

단일 스레드(동일한 스레드에서 송수신): 메시지가 메시지 큐에 들어갑니다. 스레드가 메시지 큐에 들어가면 큐에 있는 메시지에 해당하는 슬롯 기능을 순차적으로 실행합니다.

크로스 스레드(서로 다른 스레드 송수신): 메시지가 수신자 스레드 큐에 들어갑니다. 수신 스레드가 실행 중일 때(차단, 절전 또는 종료) 메시지 대기열에 들어간 후 대기열의 메시지가 순차적으로 실행됩니다. 이는 또한 동일한 슬롯 기능이 재진입되지 않으며 재진입 상호 배제 액세스를 고려할 필요가 없음을 의미합니다. 왜냐하면 모두 순차적으로 실행될 대기열에 대기 중이기 때문입니다(postEvents를 사용하지 않도록 주의하십시오). 기능이 무분별하게 작동하므로 이 문제에 대해서는 나중에 따로 이야기할 기회가 있을 것입니다.)

 

7) 잘못된 시연

class Thread :public QThread{

public:

Thread();

Thread(Test *outObj) { m_outObj = outObj; };

virtual ~Thread();

protected:

void run() {

emit m_outObj->sig_test(); }

private:

Test * m_outObj;

};



int main(int argc, char *argv[])

{

QCoreApplication a(argc, argv);

Test *t = new Test;

QObject::connect(t, &Test::sig_test, t, &Test::slot_test, Qt::QueuedConnection);

   Thread *thread = new Thread(t);

t->moveToThread(thread);

thread->start();

return a.exec();

}

 

위의 요소를 완전히 이해했다면 이 예제에 잘못된 점이 있습니까?

 

아래와 같이 분석합니다.

  1. 위의 예는 큐 연결이며, 슬롯 함수는 수신자가 위치한 스레드(who)에서 슬롯 함수를 실행하고 스레드가 이벤트 루프(when)로 돌아올 때까지 대기합니다.
  2. T가 moveToThread를 호출한 후 수신자인 t가 하위 스레드로 이동되었습니다.
  3. 자식 스레드가 start를 실행하고 실행에서 방출 신호가 호출된 후 자식 스레드는 슬롯 함수를 즉시 실행하지 않고 대신 자식 스레드가 이벤트 루프로 돌아올 때까지 슬롯 함수를 실행하지 않습니다. 그러나 이때 자식 스레드는 바로 종료되며 자식 스레드의 이벤트 루프로 돌아갈 기회가 없습니다.

결국 슬롯 함수는 실행할 기회가 없습니다.

 

8) QT 다중 스레드 프로그래밍에 대한 참고 사항

1 QT 기본 스레드를 차단할 수 없습니다. UI가 메인 스레드에 있기 때문에 차단되면 인터페이스가 중단됩니다. 절전 작업을 대체하려면 while() QCoreApplication::processEvents(); qapp->processEvents()를 사용하십시오. [중요한]

 

2 기본 스레드가 아닌 스레드는 UI 컨트롤을 작동할 수 없습니다. 그렇지 않으면 QT가 충돌합니다. 이는 인터페이스와 로직을 분리하는 중요한 이유이기도 하다. [중요한]

 

3 부모-자식 QObject 개체는 동일한 스레드에 있어야 하며 다른 스레드의 개체는 부모-자식 관계에 있을 수 없습니다.

그렇지 않으면 오류가 보고되거나 알 수 없는 상황이 발생합니다. [중요한]

 

4 공식적인 우선 순위는 moveToThread 메서드를 사용하고 상속된 QThread 메서드를 사용하는 것입니다. 이해한 후에도 마찬가지입니다.

 

5 슬롯 함수는 스레드에서 실행됩니다. 실행 스레드가 잠자고 차단되면 슬롯 함수가 실행될 기회가 없습니다. 슬롯이 빨라질 경우 차단하거나 휴면할 수 있는 스레드에 슬롯을 두지 마십시오. [중요한]

 

6 공유 변수를 수정하려면 voldate에 주의하고 잠금에 주의하십시오. 다른 잠금 동작은 스레드의 다른 상태로 이어지므로 스레드 비즈니스 상태에 따라 사용할 잠금을 고려해야 합니다. [중요한]

 

7 주의해서 processEvents 사용

스레드(메인 스레드 및 기타 스레드 포함)는 무거운 컴퓨팅 작업을 수행하며, 메시지 이벤트가 처리될 기회가 없는 것을 방지하기 위해 함수에서 processEvents를 수동으로 호출하여 메시지 이벤트가 업데이트될 수 있도록 합니다. 인터페이스가 정지되지 않습니다.

반면에 processEvents는 슬롯 함수에서 호출할 수 없습니다. 이는 현재 슬롯 함수의 "중단"을 유발하고 메시지 대기열에서 후속 슬롯 함수를 실행하여 동일한 슬롯 함수의 재진입 문제를 일으킬 수 있기 때문입니다. 프로그래밍 문제를 복잡하게 만듭니다.

 

8 어떤 새로운 Qobject 개체를 수동으로 해제해야 합니까?

QObject객체는 부모노드가 있으면 수동삭제 없이 해제가 가능하며, 부모노드가 해제되면 모든 자식노드는 자동으로 해제되며, 그렇지 않은 경우에는 부모노드가 없을 경우 다음을 호출하여 수동으로 해제해야 합니다. 삭제.

QObject를 삭제할 때 객체는 부모 노드에서 제거된 다음 모든 자식 노드를 삭제하고 해제합니다. 일부 addChild 작업은 개체를 부모 노드에 능동적으로 추가합니다. 부모-자식 관계는 필수 불가결한 관계가 아니며 개체 메모리 재활용과 관련되므로 이를 알고 있어야 합니다.

 

  1. 발문

최근 몇 년 동안 QT 개발을 해왔다고 주장하는 일부 프로그래머를 인터뷰했는데, 그들 중 일부는 QT의 다섯 번째 매개변수에 대해 들어본 적이 없거나 들어봤지만 그 원리를 깊이 이해하지 못했습니다. 이 사람들은 일상 업무에서 깊이 생각하지 않았거나 결론에 깊이 들어가지 않았음을 생각할 수 있습니다. 아마도 더 많은 사람들이 이러한 중요한 문제를 접할 기회가 없을 것입니다. 낮은 수준의 내부 반복을 수행하느라 너무 바쁘기 때문입니다.

이 기사는 순전히 "질병 치료 및 생명 구하기"를 위해 작성되었습니다. 제 한계가 있는 관계로 틀린 부분이 있으면 지적해 주시고 의논해 주시기 바랍니다.

추천

출처blog.csdn.net/peterbig/article/details/106946291