C++에서 스레드 안에 스레드 넣을 수 있나요?
조회수 1766회
스레드 안에 스레드를 넣을 수 있을까요?
지금 파이썬은 스레드 안에 스레드 넣으면 (밖의 스레드를 A 스레드 안의 스레드를 B라고 하면)
AB / AB / AB / AB... 이런 식으로 반복이 되거든요 (A와 B스레드 모두 안에 while True 무한루프 있음)
근데 C++은 스레드 안에 스레드를 넣어봤는데
ABBBBBBBBBBBBBBBBBBBBBBB.... 이렇게 출력 됩니다. 즉, 처음 스레드는 한 번만 되고 스레드 안의 스레드만 무한 반복 되는 꼴이라는건데요.
파이썬 처럼 AB / AB / AB / AB... 이렇게 반복 되게는 할 수 없을까요?
스레드 안의 스레드를 detach로 하더라도 AAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.... 이렇게 나오더라구요.
가장 좋은 경우는 A 따로, B따로 무한루프를 돌게 하는 건데.. 문제는 main에서 바로 스레드를 돌리지 못 합니다.
지금 굳이 왜 A안에 B를 넣었느냐면, A에서 다른 클라이언트로부터 받은 정보가 전달인자로 들어가 B의 스레드가 실행되어야 하는 상황입니다. (A는 항상 recv가 가능한 Server 코드입니다.)
1 답변
-
fiber와 같은 특수 상황이 아닌 이상, 쓰레드 안 쓰레드란 개념은 없습니다.
아래 처럼 한 쓰레드의 스택 내 변수에 다른 쓰래드의 핸들이 존재한다는 의미로 생각 됩니다. 이를 쓰레드 안 쓰레드라고 부르지 않습니다.
void funcB() { while (true){ // ... } } void funcA() { std::thread b(funcB); b.detach(); while (true){ // ... } } int main() { std::thread a(funcA); a.join(); return 0; }
ABBBBBBBBBBBBBBBBBBBBBBB.... 이렇게 출력 됩니다. 즉, 처음 스레드는 한 번만 되고 스레드 안의 스레드만 무한 반복 되는 꼴이라는건데요.
OS에서 동작하는 모든 응용 프로그램의 쓰레드는 OS에 의해 스케줄링됩니다. 어떤 쓰레드가 어느 코어에서 얼마만큼의 시간을 할당받아 동작할 지는 OS가 결정하게 됩니다. 이는 C++던 Python 이던 모두 동일합니다.
ABBBBBBBBBBBBBBBBBBBBBBB
와 같이 동작한다고 한다면 이는 OS가 아직 쓰레드 A를 실행해야한다고 결정하지 않았기 때문입니다. OS는 왜 그런 판단을 하였냐고 한다면, 쓰레드의 스케줄링은 while 루프 한번을 기준으로 이루어지는 것이 아니기 때문입니다.기존적인 방법으로 일정 시간 단위(time quantum)로 각각의 쓰레드를 돌아가면서 실행을 하게 됩니다. 만약 이 시간단위가 1ms이라서 쓰레드 A와 B가 1ms 씩 CPU를 사용할 시간을 할당 받아 동작하는데, while 루프 한번 도는데 걸리는 시간이 0.0001ms 라면 쓰레드 B가 1000번 루프를 돌고 쓰레드 A가 동작할 것입니다.
두 쓰레드가 경쟁상태가 아닌 이상 한 쓰레드가 일방적으로 동작하지는 않습니다.
파이썬 처럼 AB / AB / AB / AB... 이렇게 반복 되게는 할 수 없을까요?
OS는 쓰레드를 루프 한번 기준으로 돌아가며 동작 시키는 것이 아니기에 "다른 쓰레드를 동작 시켜줘" 라고 알려줄 필요가 있습니다.
- 가장 간단한 방법으로는 std::this_thread::yield()를 사용하는 방법
이는 OS에게 CPU사용을 다했으니 다른 쓰레드에게 양보하겠다는 의미입니다. 이 함수가 호출되면 OS는 아직 다른 쓰레드로 전환할 시간이 아니지만 현재 쓰레드의 실행을 중지하고 다른 쓰레드를 실행합니다.
하지만
yield()
를 호출 한다고해서 OS가 반드시 이를 따르는 것은 아니기에 정확히 AB/AB/AB 순으로 동작하지는 않습니다.- std::condition_variable을 사용하는 방법
condition_variable
는 쓰레드간 흐름 동기를 맞추기 위해서 사용합니다. 이 것이 제공하는 기능중에 알림 기능이 있습니다.아래 코드와 같이 사용할 수 있으나, 이는 두 쓰레드가 번갈아가면서 실행되기 때문에 한 쓰레드는 다른 쓰레드의 완료를 기다리게 됩니다.
std::mutex mutex; std::condition_variable cv; bool flag = true; void funcB() { while (true) { std::unique_lock<std::mutex> lock(mutex); cv.wait(lock, []() { return !flag; }); // ... flag = true; lock.unlock(); cv.notify_one(); } } void funcA() { std::thread b(funcB); b.detach(); while (true){ std::unique_lock<std::mutex> lock(mutex); cv.wait(lock, []() { return flag; }); // ... flag = false; lock.unlock(); cv.notify_one(); } } int main() { std::thread a(funcA); a.join(); return 0; }
추가로 Python 에서는 AB / AB / AB / AB... 으로 동작했는지를 추측해보면 GIL 때문이 아닐까 생각합니다.
댓글 입력