안녕하세요. 안드로이드 10기 이진성입니다.
본 포스팅은 코드스피츠 88 - async, await 의 개념 설명 부분만을 정리한 내용입니다.
Sync는 동기적으로 처리한다는 것을 의미합니다. 이를 정확히 짚기 위해서 Sync Flow Control에 대해 알아보자.
메모리에 적재된 명령이 순차적으로 실행됩니다. 우리는 적재된 명령이 순차적으로 소비되는 것에 관여할 수 없습니다.
명령이 시작되면 우리는 순서에 관여할 수 없습니다. 하지만 Sync Flow Control를 이용해서 처음에 명령을 만들때 부터 다른 위치에 명령이 실행될 수 있도록 미리 짜놓을 수 있습니다. 우리가 코드에서 If와 For문을 사용해 미리 프로그래밍을 하는 것처럼 명령이 시작하기 전부터 어떻게 Flow가 바뀔지 미리 정해놓은 것을 의미합니다. Goto를 통해 명령의 위치를 이동할 수 있고 If와 For문 같은 제어문을 사용할 수 있습니다.
함수를 통해 별도의 명령셋을 여러번 실행합니다. Sync Flow가 진행되고 있는데 Sub Flow가 실행되고 다시 Sync Flow로 돌아오는 것으로 우리가 사용하는 코드에서 함수 호출에 가깝다고 할 수 있다.
-> 우리는 일반적으로 Sync Flow Control를 사용해 코드를 짠다고 말할 수 있다.
Sync Flow가 실행되는 동안 다른 일을 할 수 없는 현상입니다. 즉 Sync Flow 는 무조건 Blocking을 일으킵니다. 다시 말하자면 Blocking은 Sync가 Flow 일어나는 사이에 CPU가 먹통이 되는 것이라고 할 수 있습니다.
Blocking은 피할 수 없는 현상이므로 우리는 이를 줄이는데 노력해야 한다. 어떻게 Blocking 현상을 줄일 수 있을까?
코드 한줄 한줄이 sync flow 라고 할 수 있습니다. 만약 코드 7줄을 3줄로 줄인다면 Blocking을 줄일 수 있지만 코드가 7줄인 이유가 있을 겁니다. 그러므로 코드 줄을 짧게 작성하는 것은 바람직 하지만 불가능하죠. 물론 코드를 짧게 작성하거나 루프를 짧게 작성하기 위한 방법이나 알고리즘을 도입하면 Blocking 구간은 짧아질 수 있습니다.
다른 쓰레드를 사용하면 쓰레드 하나당의 Blocking 구간이 짧아집니다.
코어는 CPU를 물리적으로 구별한 것이고, 쓰레드는 CPU를 논리적으로 구별한 것입니다. 일반적으로 코어 갯수와 쓰레드 갯수는 동일하지만 1개의 코어가 2개 이상의 쓰레드를 사용할 수 있습니다. 이를 SMT 기술이라고 부르며 인텔에서는 하이퍼스레딩이라고도 합니다.
코어를 검색해 보면 8코어 16쓰레드로 나와있는 제품을 볼 수 있다.
운영체제가 스케쥴링을 할 때, ‘동시에 실행 가능한 쓰레드 수’, 즉 물리적으로 할당 가능한 쓰레드는 정해져 있습니다. 하지만 메모리가 허용하는 한 ‘소프트웨어적 쓰레드 수’는 얼마든지 많을 수 있습니다. 운영체제에 의해 실행되지 않은 쓰레드는 잠들어 있을 수 있으며 이들 중 물리적으로 허용 가능한 만큼의 쓰레드를 할당하여 동시에 실행합니다
정리하자면 소프트웨어적 쓰레드는 프로세스 처리의 세부 단위로 프로그램에서 얼마든지 할당할 수 있습니다. 하지만 이를 동시에 실행시키는 것은 코어의 몫입니다.
다시 본문으로 돌아와서 다른 쓰레드에 sync flow를 떠넘기면 어떤 문제가 있을까요?
Sync Flow가 납득할 만한 시간 내에 종료되는 것을 의미합니다.
한 예로 구구단 for문을 실행하는 로직이 있습니다. 이를 Blocking 이라고 할까요 아님 NonBlocking 이라고 해야 할까요? 구식 컴퓨터를 사용할 경우 우리가 체감할 정로도 느리게 느껴질 꺼고 그렇다면 이를 블로킹이라 할 수 있습니다. 하지만 나노초 만큼의 시간, 즉 우리가 느끼지도 못할 만큼 빠르게 동작하면 논블로킹이라고 할 수 있습니다.
블로킹과 논블로킹의 차이는 블로킹되는 시간이 내가 맘에 드는 만큼 짧은가? 로 볼 수 있습니다. 그렇다면 이는 하드웨어 혹은 개인마다 다를 것이지만 통상적으로 프론트 기준으로는 16ms 안에 들어오는걸 논블로킹 이라고 합니다. 즉 프레임당 블로킹이 16ms 안에 들어오면 화면에 60 프레임으로 그릴 수 있고 이는 우리가 인식 못하는 수준의 속도입니다. 하지만 게임을 예로 들자면 게이밍 모니터는 144Hz를 사용하다보니 화면 갱신률이 올라가면서 sync flow의 time budge이 내려갑니다. 이 경우 144Hz 화면에서는 한 프레임당 8~6 ms 안에 들어와야 논블로킹이라고 할 수 있습니다.
싱크와 어싱크는 멀티 스레드하고 관계 없고, 비동기 프레임하고도 관계 없습니다. 오직 함수가 값을 직접 반환하는가 다른 수단으로 반환하는가 차이다.
Sync와 Async는 Blocking이랑 완전히 관련이 없다. Async는 그저 함수가 값을 어떻게 반환하는가만 관련이 있다. 그러므로 Async Blocking 가능하고 Async NonBlocking 도 가능하다. 일반적으로 함수를 작성하면 싱크 블로킹이고 충분히 짧게 동작하는 함수를 작성하면 싱크 논블로킹이라고 할 수 있다. 여기서 블로킹을 논블로킹으로 만들러면 sync flow를 짧게 작성하든지 멀티 쓰레드를 사용하면 된다.
코드스피츠 영상 중 개념 부분만을 정리한 내용입니다. 영상 뒷부분은 1편과 2편으로 나눠 코드 위주로 설명되어 있습니다. 제가 영상을 보며 느낀점은 Sync와 Async 그리고 Blocking과 NonBlocking을 우리와 생각하기 쉬운 개념으로 설명해서 이해하기 편했습니다. 다른 문서를 보면 대게 제어권을 누가 갔느냐? 관심사를 어떻게 분리하는가? 등의 내용으로 읽어도 뭔가 와닿는 느낌이 없었습니다. 쉬운 개념 설명과 다양한 코드 예제를 보니 뭔가 알것 같지만 이를 제 코드에 적용해 보면서 꾸준히 공부해 나가야겠죠?