이 페이지에서

스레드와 QObject

QThread QObject 를 상속받습니다. 이 클래스는 스레드 실행이 시작되거나 완료되었음을 나타내는 신호를 발산하며, 몇 가지 슬롯도 제공합니다.

더 흥미로운 점은 QObject가 여러 스레드에서 사용될 수 있고, 다른 스레드의 슬롯을 호출하는 신호를 발산하며, 다른 스레드에 “존재”하는 객체에 이벤트를 게시할 수 있다는 것입니다. 이는 각 스레드가 자체 이벤트 루프를 가질 수 있기 때문에 가능합니다.

QObject 재진입성

QObject 는 재진입 가능합니다. QTimer, QTcpSocket, QUdpSocket, QProcess 과 같은 대부분의 비-GUI 하위 클래스들도 재진입 가능하므로, 여러 스레드에서 동시에 이러한 클래스를 사용할 수 있습니다. 단, 이러한 클래스들은 단일 스레드 내에서 생성 및 사용되도록 설계되었으므로, 한 스레드에서 객체를 생성하고 다른 스레드에서 해당 함수를 호출하는 경우 정상 작동이 보장되지 않습니다. 주의해야 할 세 가지 제약 사항이 있습니다:

  • QObject 의 자식 객체는 반드시 부모 객체가 생성된 스레드에서 생성되어야 합니다. 이는 무엇보다도, QThread 객체(this)를 해당 스레드에서 생성된 객체의 부모로 절대 전달해서는 안 된다는 것을 의미합니다( QThread 객체 자체가 다른 스레드에서 생성되었기 때문입니다).
  • 이벤트 기반 객체는 단일 스레드에서만 사용해야 합니다. 특히, 이는 타이머 메커니즘과 network module 에 적용됩니다. 예를 들어, object's thread 이 아닌 스레드에서는 타이머를 시작하거나 소켓을 연결할 수 없습니다.
  • QThread 를 삭제하기 전에 해당 스레드에서 생성된 모든 객체가 삭제되었는지 반드시 확인해야 합니다. 이는 run() 구현에서 객체를 스택에 생성함으로써 쉽게 처리할 수 있습니다.

QObject 는 재진입 가능하지만, GUI 클래스, 특히 QWidget 및 그 모든 하위 클래스는 재진입 가능하지 않습니다. 이들은 메인 스레드에서만 사용할 수 있습니다. 앞서 언급했듯이, QCoreApplication::exec()도 해당 스레드에서 호출되어야 합니다.

실제로 메인 스레드 이외의 스레드에서 GUI 클래스를 사용할 수 없다는 점은, 시간이 많이 소요되는 작업을 별도의 작업자 스레드에 배치하고 작업자 스레드가 완료되면 메인 스레드에서 결과를 화면에 표시함으로써 쉽게 해결할 수 있습니다. 이는 만델브로트 예제와 Blocking Fortune Client 예제를 구현하는 데 사용된 접근 방식입니다.

일반적으로 ` QApplication `보다 먼저 `QObject`를 생성하는 것은 지원되지 않으며, 플랫폼에 따라 종료 시 예기치 않은 크래시가 발생할 수 있습니다. 즉, ` QObject `의 정적 인스턴스도 지원되지 않습니다. 올바르게 구조화된 단일 또는 다중 스레드 애플리케이션에서는 ` QApplication `가 가장 먼저 생성되고 가장 마지막에 소멸되어야 합니다. QObject

스레드별 이벤트 루프

각 스레드는 자체 이벤트 루프를 가질 수 있습니다. 초기 스레드는 ` QCoreApplication::exec()`를 사용하여 이벤트 루프를 시작하며, 단일 대화 상자 GUI 애플리케이션의 경우 때로는 ` QDialog::exec()`를 사용하기도 합니다. 다른 스레드들은 ` QThread::exec()`를 사용하여 이벤트 루프를 시작할 수 있습니다. ` QCoreApplication`와 마찬가지로, ` QThread `는 ` exit(int)` 함수와 ` quit()` 슬롯을 제공합니다.

스레드 내의 이벤트 루프를 통해 해당 스레드는 이벤트 루프의 존재를 필요로 하는 특정 비 GUI Qt 클래스(예: QTimer, QTcpSocket, QProcess)를 사용할 수 있습니다. 또한, 어떤 스레드에서든 신호를 특정 스레드의 슬롯에 연결할 수 있게 해줍니다. 이에 대한 자세한 내용은 아래의 “스레드 간 신호 및 슬롯” 섹션에서 설명합니다.

스레드, 객체 및 이벤트 루프

QObject 인스턴스는 생성된 스레드 내에 존재한다고 합니다. 해당 객체에 대한 이벤트는 해당 스레드의 이벤트 루프에 의해 디스패치됩니다. QObject 가 속한 스레드는 QObject::thread()를 사용하여 확인할 수 있습니다.

QObject::moveToThread() 함수는 객체와 그 자식 객체들의 스레드 친화성을 변경합니다(객체에 부모가 있는 경우 이동할 수 없습니다).

객체를 소유한 스레드 이외의 스레드에서 QObject 에 대해 delete 를 호출하거나(또는 다른 방식으로 객체에 접근하는 경우) 해당 시점에 객체가 이벤트를 처리하고 있지 않음을 보장할 수 없는 한, 이는 안전하지 않습니다. 대신 ` QObject::deleteLater()`를 사용하면 ` DeferredDelete ` 이벤트가 게시되며, 이 이벤트는 해당 객체의 스레드 이벤트 루프에서 결국 수신하게 됩니다. 기본적으로 ` QObject `를 소유한 스레드는 ` QObject`를 생성한 스레드이지만, ` QObject::moveToThread()`가 호출된 후에는 그렇지 않습니다.

실행 중인 이벤트 루프가 없으면 이벤트가 객체로 전달되지 않습니다. 예를 들어, 스레드 내에서 QTimer 객체를 생성했지만 exec()를 호출하지 않으면, QTimer 는 timeout() 신호를 절대 발산하지 않습니다. deleteLater()를 호출해도 작동하지 않습니다. (이러한 제한 사항은 메인 스레드에도 적용됩니다.)

스레드 안전 함수인 QCoreApplication::postEvent()을 사용하면 언제든지 어떤 스레드의 어떤 객체에도 수동으로 이벤트를 게시할 수 있습니다. 이벤트는 해당 객체가 생성된 스레드의 이벤트 루프에 의해 자동으로 디스패치됩니다.

이벤트 필터는 모든 스레드에서 지원되지만, 모니터링하는 객체는 모니터링 대상 객체와 동일한 스레드에 존재해야 한다는 제한이 있습니다. 마찬가지로, QCoreApplication::sendEvent()은 ( postEvent()과 달리) 해당 함수가 호출된 스레드에 존재하는 객체로만 이벤트를 디스패치하는 데 사용할 수 있습니다.

다른 스레드에서 QObject 하위 클래스에 접근하기

QObject 및 그 모든 하위 클래스는 스레드 안전하지 않습니다. 여기에는 전체 이벤트 전달 시스템도 포함됩니다. 다른 스레드에서 객체에 접근하는 동안 이벤트 루프가 QObject 하위 클래스에 이벤트를 전달할 수 있다는 점을 명심하는 것이 중요합니다.

현재 스레드에 존재하지 않는 QObject 하위 클래스의 함수를 호출하고, 해당 객체가 이벤트를 수신할 가능성이 있는 경우, QObject 하위 클래스의 내부 데이터에 대한 모든 액세스를 뮤텍스로 보호해야 합니다. 그렇지 않으면 충돌이나 기타 원치 않는 동작이 발생할 수 있습니다.

다른 객체와 마찬가지로, QThread 객체는 QThread::run()이 호출될 때 생성되는 스레드가아니라, 객체가 생성된 스레드에 존재합니다. QThread 서브클래스에서 슬롯을 제공하는 것은, 멤버 변수를 뮤텍스로 보호하지 않는 한 일반적으로 안전하지 않습니다.

반면, 신호 발송은 스레드 안전하므로 QThread::run() 구현에서 신호를 안전하게 발송할 수 있습니다.

스레드 간 신호 및 슬롯

Qt는 다음과 같은 신호-슬롯 연결 유형을 지원합니다:

  • Auto Connection (기본값) 신호가 수신 객체와 친화성을 갖는 스레드에서 방출되는 경우, 동작은 직접 연결(Direct Connection)과 동일합니다. 그렇지 않은 경우, 동작은 대기열 연결(Queued Connection)과 동일합니다."
  • Direct Connection 신호가 발송되면 슬롯이 즉시 호출됩니다. 슬롯은 발신자의 스레드에서 실행되며, 이는 반드시 수신자의 스레드와 일치하는 것은 아닙니다.
  • Queued Connection 슬롯은 제어권이 수신자 스레드의 이벤트 루프로 돌아올 때 호출됩니다. 슬롯은 수신자 스레드에서 실행됩니다.
  • Blocking Queued Connection 슬롯은 큐드 연결(Queued Connection)과 동일하게 호출되지만, 슬롯이 반환될 때까지 현재 스레드가 차단된다는 점이 다릅니다.

    참고: 동일한 스레드 내의 객체를 연결하는 데 이 유형을사용하면 교착 상태가 발생합니다.

  • Unique Connection 이 동작은 ‘자동 연결(Auto Connection)’과 동일하지만, 기존 연결과 중복되지 않는 경우에만 연결이 이루어집니다. 즉, 동일한 객체 쌍에 대해 동일한 신호가 이미 동일한 슬롯에 연결되어 있는 경우, 연결이 이루어지지 않고 connect()는 ` false`을 반환합니다.

connect()에 추가 인자를 전달하여 연결 유형을 지정할 수 있습니다. 송신자와 수신자가 서로 다른 스레드에 존재할 때 직접 연결을 사용하는 것은, 다른 스레드에 존재하는 객체에 대해 함수를 호출하는 것이 안전하지 않은 것과 같은 이유로, 수신자의 스레드에서 이벤트 루프가 실행 중일 경우 안전하지 않다는 점에 유의하십시오.

QObject::connect() 자체는 스레드 안전합니다.

Mandelbrot 예제는 작업자 스레드와 메인 스레드 간의 통신에 큐 연결을 사용합니다. 메인 스레드의 이벤트 루프(그리고 결과적으로 애플리케이션의 사용자 인터페이스)가 멈추는 것을 방지하기 위해, 모든 Mandelbrot 프랙탈 계산은 별도의 작업자 스레드에서 수행됩니다. 이 스레드는 프랙탈 렌더링이 완료되면 신호를 발송합니다.

마찬가지로, Blocking Fortune Client 예제는 TCP 서버와 비동기적으로 통신하기 위해 별도의 스레드를 사용합니다.

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.