스레드 동기화

스레드의 목적은 코드가 병렬로 실행되도록 하는 것이지만, 스레드를 중지하고 다른 스레드를 기다려야 하는 경우가 있습니다. 예를 들어 두 개의 스레드가 동일한 변수에 동시에 쓰기를 시도하면 결과가 정의되지 않습니다. 스레드가 서로를 기다리도록 강제하는 원리를 상호 제외라고 합니다. 이는 데이터와 같은 공유 자원을 보호하기 위한 일반적인 기술입니다.

Qt는 스레드 동기화를 위한 높은 수준의 메커니즘뿐만 아니라 낮은 수준의 프리미티브도 제공합니다.

저수준 동기화 프리미티브

QMutex 는 상호 배제를 강제하기 위한 기본 클래스입니다. 스레드는 공유 리소스에 액세스하기 위해 뮤텍스를 잠급니다. 뮤텍스가 이미 잠겨 있는 상태에서 두 번째 스레드가 뮤텍스를 잠그려고 하면 첫 번째 스레드가 작업을 완료하고 뮤텍스의 잠금을 해제할 때까지 두 번째 스레드는 절전 모드로 전환됩니다.

QReadWriteLockQMutex 와 유사하지만 "읽기" 액세스와 "쓰기" 액세스를 구분한다는 점이 다릅니다. 데이터가 쓰여지지 않을 때는 여러 스레드가 동시에 데이터를 읽는 것이 안전합니다. QMutex 에서는 여러 리더가 번갈아 가며 공유 데이터를 읽어야 하지만 QReadWriteLock 에서는 동시 읽기가 가능하므로 병렬성이 향상됩니다.

QSemaphore 는 특정 수의 동일한 리소스를 보호하는 QMutex 의 일반화입니다. 반면 QMutex 은 정확히 하나의 리소스를 보호합니다. 세마포어를 사용하는 생산자와 소비자 예시는 생산자와 소비자 간의 순환 버퍼에 대한 액세스를 동기화하는 세마포어의 일반적인 적용 사례를 보여줍니다.

QWaitCondition 는 상호 배제를 강제하는 것이 아니라 조건 변수를 제공하여 스레드를 동기화합니다. 다른 프리미티브는 리소스가 잠금 해제될 때까지 스레드를 대기시키는 반면, QWaitCondition 은 특정 조건이 충족될 때까지 스레드를 대기시킵니다. 대기 중인 스레드를 계속 진행하려면 wakeOne()를 호출하여 무작위로 선택한 스레드 하나를 깨우거나 wakeAll()를 호출하여 모든 스레드를 동시에 깨우면 됩니다. 대기 조건을 사용하는 생산자와 소비자 예제에서는 QSemaphore 대신 QWaitCondition 을 사용하여 생산자-소비자 문제를 해결하는 방법을 보여줍니다.

참고: Qt의 동기화 클래스는 적절하게 정렬된 포인터를 사용하는 것에 의존합니다. 예를 들어, MSVC와 함께 패킹된 클래스를 사용할 수 없습니다.

이러한 동기화 클래스는 메서드 스레드를 안전하게 만드는 데 사용할 수 있습니다. 그러나 그렇게 하면 성능 저하가 발생하기 때문에 대부분의 Qt 메서드는 스레드 안전하지 않습니다.

위험

스레드가 자원을 잠그고 잠금을 해제하지 않으면 다른 스레드가 해당 자원을 영구적으로 사용할 수 없게 되므로 애플리케이션이 멈출 수 있습니다. 예를 들어 예외가 발생하여 현재 함수가 잠금을 해제하지 않고 강제로 반환되는 경우 이런 일이 발생할 수 있습니다.

또 다른 유사한 시나리오는 교착 상태입니다. 예를 들어 스레드 A가 스레드 B가 리소스를 잠금 해제하기를 기다리고 있다고 가정해 보겠습니다. 스레드 B도 스레드 A가 다른 리소스를 잠금 해제하기를 기다리고 있다면 두 스레드 모두 영원히 대기하게 되어 애플리케이션이 멈추게 됩니다.

편의 클래스

QMutexLocker QReadLocker 및 은 및 을 보다 쉽게 사용할 수 있도록 해주는 편의 클래스입니다. 이 클래스는 리소스가 생성될 때 잠그고, 소멸될 때 자동으로 잠금을 해제합니다. 및 을 사용하는 코드를 간소화하여 리소스가 실수로 영구적으로 잠길 가능성을 줄이도록 설계되었습니다. QWriteLocker QMutex QReadWriteLock QMutex QReadWriteLock

고수준 이벤트 큐

Qt의 이벤트 시스템은 스레드 간 통신에 매우 유용합니다. 모든 스레드에는 자체 이벤트 루프가 있을 수 있습니다. 다른 스레드에서 슬롯(또는 invokable 메서드)을 호출하려면, 해당 호출을 대상 스레드의 이벤트 루프에 배치합니다. 이렇게 하면 대상 스레드가 슬롯이 실행되기 전에 현재 작업을 완료하고 원래 스레드는 계속 병렬로 실행할 수 있습니다.

이벤트 루프에 호출을 배치하려면 대기 중인 신호-슬롯 연결을 만듭니다. 신호가 방출될 때마다 인수가 이벤트 시스템에 기록됩니다. 그러면 신호 수신자인 lives in 스레드가 슬롯을 실행합니다. 또는 QMetaObject::invokeMethod()를 호출하여 신호 없이도 동일한 효과를 얻을 수 있습니다. 두 경우 모두 direct connection 은 이벤트 시스템을 우회하여 현재 스레드에서 메서드를 즉시 실행하므로 queued connection 을 사용해야 합니다.

스레드 동기화에 이벤트 시스템을 사용할 때는 저수준 프리미티브를 사용할 때와 달리 교착 상태의 위험이 없습니다. 그러나 이벤트 시스템은 상호 배제를 강제하지 않습니다. 호출 가능한 메서드가 공유 데이터에 액세스하는 경우에도 여전히 저수준 프리미티브로 보호해야 합니다.

그렇긴 하지만, Qt의 이벤트 시스템은 암시적으로 공유되는 데이터 구조와 함께 기존의 스레드 잠금에 대한 대안을 제공합니다. 신호와 슬롯을 독점적으로 사용하고 스레드 간에 변수를 공유하지 않는다면, 멀티스레드 프로그램은 로우레벨 프리미티브 없이도 실행할 수 있습니다.

QThread::exec() 및 스레드와 QObject를참조하십시오 .

© 2025 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.