해결된 질문
작성
·
48
0
안녕하세요.
강의를 통해 많은 도움을 받고 있습니다.!
다름이 아니라, 17강 - 11분20초 부분에서
"작업의 결과를 모을 때는 하나의 thread에서만 동작하게 됩니다. 예를 들자면 2번 thread 하나에서만 비동기 반복문이 동작하게 되는거에요" 라고 설명을 해 주셨는데
제가 이해한 바로는 'Swift Concurrency는 thread관점에서 벗어나서, Task라는 작업의 단위를 기준으로 비동기 관리를 한다' 라고 이해하고 있습니다.
때문에, "비동기 반복문에서도 await을 통해 비동기 결과를 받고 있는데, 이 때 특정 thread에 고정된다는 것이 보장 될 수 있는건가?" 하는 궁금증이 생겨서 질문드립니다!
만약 하나의 thread에 고정되어 있다면, group을 통해서 결과가 넘어오게 될 때, 자식 Task중 과도하게 오래걸리는 작업이 있다고 가정하면 비동기 반복문이 실행되는 특정 thread가 계속 blocking되는건가? 하는 의문이 들어서요,,,!
답변 2
1
네, 안녕하세요 요니 님!
제가 이해한 바로는 'Swift Concurrency는 thread관점에서 벗어나서, Task라는 작업의 단위를 기준으로 비동기 관리를 한다' 라고 이해하고 있습니다.
==> 네, Task라는 작업의 단위를 기준으로 관리한다는 말은 맞지만, 결국 그 내부 메커니즘을 파고 들어가보면 Task 내부에서는 1개의 쓰레드(1개의 CPU)를 사용하는 것입니다. (수업에서 그려드린 그림을 잘 참고해보시면 좋고요.) 그리고 Swift Concurrency 자체는 내부적으로 (운영체제에서.. 현재 사용하고 있지 않은 쓰레드를 양보하는) 협력적 Thread Pool 방식으로 동작합니다. (기존의 GCD에서 Swift Concurrency가 도입된 것은.. 이 협력적 Thread Pool 방식(쓰레드를 양보하는)이 훨씬 내부적으로 효율적으로 동작하게 만들어주는 부분이 있기 때문에 이 방식이 도입된 것입니다. 이 부분을 조금 더 자세하게 설명드리려면.. 내용이 너무 길어지기 때문에.. Part2강의의 CS부분을 참고하시면 많은 도움이 되실 것 같긴 합니다. 쓰레드는 편하게 CPU 1개라고 생각하셔도 됩니다.)
그래서 Task는 말 그대로.. "일"의 단위 정도로 생각하시면 됩니다. 예를 들어 작업(Task)은 언제든지 생성, 소멸 같은 일이 발생할 수 있는 것이고, 그 안에서 결국 운영체제가 생성한 Thread를 사용하게 되는 것입니다.
최대한 직관적으로, TaskGroup이라는 자체가 도입된 이유를 무엇일까?를 생각해보시면.. 결국 이러한 여러개의 Task가 (여러개의 쓰레드(CPU)를 사용해) 동시에 일을 시키도록 만들고, 마지막에는.. 그러한 작업들을 한데 모아서 처리하려는 목적을 가지고 설계된 것이 TaskGroup이라고 보시면 됩니다. (쉬운 병렬 작업과, 마지막에는 Thread-safe한 직렬이 필요한 것이죠.)
만약 하나의 thread에 고정되어 있다면, group을 통해서 결과가 넘어오게 될 때, 자식 Task중 과도하게 오래걸리는 작업이 있다고 가정하면 비동기 반복문이 실행되는 특정 thread가 계속 blocking되는건가? 하는 의문이 들어서요
==> 결론적으로는 Non-Blocking방식이기 때문에 오래걸리는 작업이 있다고 하더라도, blocking되지 않습니다. 사실 이 부분이 Non-Blocking 방식의 장점인데, (추후의 Part2의 CS부분까지 학습하게되시면 내부적인 메커니즘을 더 정확하게 알게 되실텐데..) 무튼 최대한 직관적으로 설명드리면, 제가 Non-blocking을 설명드리면서 그려드렸던.. 아래와 같은 그림을 떠올려보시면 좋습니다. 오래걸리는 작업이 있다고 하더라도 (일을 안하는 동안에) 쓰레드를 양보하기 때문에 Blocking이 일어날 일이 없습니다. (그게 사용하지 않는 동안에 쓰레드를 양보하는 협력적 Thread Pool이 동작하는 방식이자 Swift Concurrency의 장점이죠.)
그리고 이 부분을 최대한 직관적으로 이해하시기 좋게.. 제가 이걸 TaskGroup에 적용해서 (오래 걸리는 작업이 있다고 가정하고 그림을 그려서 보여드리면..)
이런식으로 하위 자식 작업이 오래 걸리는 작업이 있다고 하더라도 결국 부모작업 조차도 자기가 동작하지 않는 동안에 쓰레드를 양보하게 됩니다. (Non-Blocking 방식) 그렇기 때문에 Thread에서 Blocking이 일어날 수 없습니다.
뭐 그렇다면 한가지 의문이 드실 수도 있는데요.. 그렇다면, 부모작업이 반드시 2번 쓰레드에서만 동작하는 것이냐? 이렇게 의문을 가지실 수도 있는데.. 물론 (엄밀하게 따지면) 그렇지는 않습니다. 부모작업 내부적으로 (작업 재개를 할 때) 2번 쓰레드를 사용할 수도 있고, 3번 쓰레드를 사용할 수도 있는데.. 사실, 그건 우리에게 중요한 관심사는 아닙니다. 왜냐면, (2번 쓰레드이던, 3번 쓰레드이던, 4번쓰레드이던..) (동시에 동작하는 일은 절대 발생하지 않고) 순차적으로(무조건 차례 차례) 1개의 특정 쓰레드에서만 (직렬방식으로) 일을 하면서 Thread-safe하게 일을 하게 되기 때문에.. 그게 (내부적으로 꼭 정해진 특정 쓰레드인) 2, 3, 4번 이던 간에 그게 중요한 것은 아니라는 것이죠. 다만, 그렇기 때문에 저는 최대한 직관적으로 생각하시기 편하게.. 부모 작업은 "특정" 2번 쓰레드에서만 동작하게 된다라고 설명드린 것이라고 보시면 됩니다.
추가적으로 궁금하신 부분 있으시면 또 질문 주세요.
감사합니다. :)
0
안녕하세요, 인프런 AI 인턴입니다. 요니님이 Swift Concurrency와 관련하여 비동기 반복문의 작동 방식에 대해 질문 주셨네요. 질문에 대해 자세히 답변드리겠습니다.
Swift Concurrency는 전통적인 스레드 기반의 동시성 모델에서 탈피하여, 더 높은 수준의 추상화된 Task 기반으로 작동합니다. 이는 비동기 작업을 수행할 때 직접적으로 스레드를 관리하기보다는 Task 단위로 작업을 나누어 수행하게 함으로써, 개발자가 스레드 관리의 복잡함을 덜 수 있도록 돕습니다.
비동기 반복문(for await
)은 내부적으로 Task의 일부분으로 처리되며, 이는 특정 스레드가 아닌 Task들의 실행 컨텍스트에 따라 관리됩니다. 따라서 비동기 반복문이 항상 동일한 스레드에서 실행된다고 보장되지 않습니다. Swift Concurrency는 작업을 효율적으로 처리하기 위해 필요에 따라 다른 스레드에 Task를 재할당할 수 있습니다.
질문 주신 대로, 특정 스레드에 고정된다면 자식 Task 중 하나가 오래 걸릴 경우 해당 스레드가 차단될 수 있습니다. 그러나 Swift Concurrency는 스레드 풀을 사용하여 이러한 차단을 최소화하도록 설계되어 있습니다. 즉, Swift는 내부적으로 Task가 오래 걸리거나 차단되지 않도록 스케줄링하여 작업을 분산 처리합니다.
따라서, Swift Concurrency에서는 비동기 반복문이 항상 하나의 스레드에서 실행된다고 가정할 필요는 없습니다. Task 기반의 동시성 모델은 스레드 차단을 최소화하고 최적의 성능을 유지하기 위해 필요한 경우 다른 스레드로 작업을 이동시킬 수 있습니다.
관련하여 Swift Concurrency와 비동기 Task에 대한 더 많은 정보를 다루고 있는 Apple의 공식 문서를 참조할 수 있습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
빠르고 자세한 답변 감사드립니다!!
해당 이미지와 설명을 들으니, 직관적으로 이해가 된 것 같습니다 ㅎㅎ
감사합니다!!