스레드 동기화
스레드의 목적은 코드를 병렬로 실행하는 것이지만, 때로는 스레드가 일시 중지되어 다른 스레드를 기다려야 할 때가 있습니다. 예를 들어, 두 스레드가 동시에 동일한 변수에 쓰기를 시도하면 결과는 정의되지 않습니다. 스레드들이 서로를 기다리도록 강제하는 원리를 상호 배제(mutual exclusion)라고 합니다. 이는 데이터와 같은 공유 자원을 보호하기 위한 일반적인 기법입니다.
Qt는 스레드 동기화를 위한 고수준 메커니즘뿐만 아니라 저수준 프리미티브도 제공합니다.
저수준 동기화 기본 요소
QMutex 는 상호 배제를 시행하기 위한 기본 클래스입니다. 스레드는 공유 리소스에 접근하기 위해 뮤텍스를 잠급니다. 뮤텍스가 이미 잠겨 있는 상태에서 두 번째 스레드가 뮤텍스를 잠그려고 하면, 첫 번째 스레드가 작업을 완료하고 뮤텍스의 잠금을 해제할 때까지 두 번째 스레드는 대기 상태가 됩니다.
QReadWriteLock 는 뮤텍스( QMutex)와 유사하지만, “읽기”와 “쓰기” 액세스를 구분한다는 점이 다릅니다. 데이터에 쓰기 작업이 진행 중이 아닐 때는 여러 스레드가 동시에 해당 데이터를 읽는 것이 안전합니다. 뮤텍스( QMutex )는 여러 읽기 스레드가 공유 데이터를 번갈아 읽도록 강제하지만, 뮤텍스( QReadWriteLock )는 동시 읽기를 허용하여 병렬 처리를 향상시킵니다.
QSemaphore 는 QMutex 의 일반화된 형태로, 일정 수의 동일한 리소스를 보호합니다. 반면, QMutex 는 정확히 하나의 리소스만 보호합니다. ‘세마포어를 사용하는 프로듀서와 컨슈머’ 예제는 세마포어의 전형적인 적용 사례로, 프로듀서와 컨슈머 간의 순환 버퍼에 대한 액세스를 동기화하는 방법을 보여줍니다.
QWaitCondition 조건 변수( condition variable)는 상호 배제를 강제하는 대신 스레드를 동기화합니다. 다른 기본 요소들이 리소스가 잠금 해제될 때까지 스레드를 대기시키는 반면, 조건 변수( QWaitCondition )는 특정 조건이 충족될 때까지 스레드를 대기시킵니다. 대기 중인 스레드가 진행하도록 하려면, wakeOne()을 호출하여 무작위로 선택된 하나의 스레드를 깨우거나, wakeAll()을 호출하여 모든 스레드를 동시에 깨울 수 있습니다. 'Wait Conditions를 사용하는 프로듀서 및 컨슈머' 예제는 QSemaphore 대신 QWaitCondition 을 사용하여 프로듀서-컨슈머 문제를 해결하는 방법을 보여줍니다.
참고: Qt의 동기화 클래스는 올바르게 정렬된 포인터를 사용해야 합니다. 예를 들어, MSVC에서는 packed 클래스를 사용할 수 없습니다.
이러한 동기화 클래스를 사용하면 메서드를 스레드 안전하게 만들 수 있습니다. 그러나 이렇게 하면 성능 저하가 발생하기 때문에 대부분의 Qt 메서드는 스레드 안전하게 만들어지지 않습니다.
위험
한 스레드가 리소스를 잠갔다가 잠금을 해제하지 않으면, 해당 리소스를 다른 스레드에서 영구적으로 사용할 수 없게 되어 애플리케이션이 멈출 수 있습니다. 예를 들어, 예외가 발생하여 현재 함수가 잠금을 해제하지 않은 채 강제로 반환되는 경우 이러한 현상이 발생할 수 있습니다.
이와 유사한 또 다른 시나리오는 교착 상태입니다. 예를 들어, 스레드 A가 스레드 B가 리소스의 잠금을 해제하기를 기다리고 있다고 가정해 봅시다. 만약 스레드 B도 스레드 A가 다른 리소스의 잠금을 해제하기를 기다리고 있다면, 두 스레드 모두 영원히 기다리게 되어 애플리케이션이 멈추게 됩니다.
편의 클래스
QMutexLocker, ` QReadLocker ` 및 ` QWriteLocker `은 ` QMutex ` 및 ` QReadWriteLock`을 더 쉽게 사용할 수 있도록 해주는 편의 클래스입니다. 이 클래스들은 생성될 때 리소스를 잠그고, 소멸될 때 자동으로 잠금을 해제합니다. 이 클래스들은 ` QMutex ` 및 ` QReadWriteLock`을 사용하는 코드를 단순화하여, 실수로 인해 리소스가 영구적으로 잠기는 가능성을 줄이도록 설계되었습니다.
고수준 이벤트 큐
Qt의 이벤트 시스템은 스레드 간 통신에 매우 유용합니다. 각 스레드는 자체 이벤트 루프를 가질 수 있습니다. 다른 스레드에서 슬롯(또는 invokable 메서드)을 호출하려면, 해당 호출을 대상 스레드의 이벤트 루프에 배치하십시오. 이렇게 하면 슬롯이 실행되기 전에 대상 스레드가 현재 작업을 완료할 수 있으며, 원본 스레드는 병렬로 계속 실행됩니다.
호출을 이벤트 루프에 배치하려면 큐에 추가된 시그널-슬롯 연결을 생성하십시오. 신호가 발송될 때마다 그 인수는 이벤트 시스템에 기록됩니다. 이후 신호 수신자( lives in )가 속한 스레드에서 슬롯이 실행됩니다. 또는 QMetaObject::invokeMethod()를 호출하여 신호 없이도 동일한 효과를 얻을 수 있습니다. 두 경우 모두 queued connection 를 사용해야 합니다. direct connection 는 이벤트 시스템을 우회하여 현재 스레드에서 메서드를 즉시 실행하기 때문입니다.
저수준 프리미티브를 사용하는 것과는 달리, 스레드 동기화를 위해 이벤트 시스템을 사용할 때는 교착 상태의 위험이 없습니다. 그러나 이벤트 시스템은 상호 배타성을 강제하지 않습니다. 호출 가능한 메서드가 공유 데이터에 접근하는 경우, 여전히 저수준 프리미티브를 사용하여 보호해야 합니다.
그렇긴 하지만, Qt의 이벤트 시스템은 암시적으로 공유되는 데이터 구조와 함께 기존의 스레드 잠금 방식에 대한 대안을 제공합니다. 시그널과 슬롯만을 사용하고 스레드 간에 변수를 공유하지 않는다면, 멀티스레드 프로그램은 저수준 프리미티브를 전혀 사용하지 않고도 작동할 수 있습니다.
QThread::exec() 및 스레드와 QObject도참조하십시오 .
© 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.