[iOS] 차근차근 시작하는 GCD — 15
Previously on GCD…
우리는 [iOS] 차근차근 시작하는 GCD — 11 부터 여러개의 스레드에서 동시에 일을 진행할 때 발생할 수 있는 문제, 즉 동시성 문제(Concurrency Problem)에 대해 다뤄오고 있습니다. 아래 세가지 케이스 중 경쟁 상태, 교착 상태는 모두 알아봤으니
- Race Condition (경쟁 상태)
- Deadlock (교착상태)
- Priority Inversion (우선 순위의 뒤바뀜)
마지막으로 우선 순위의 뒤바뀜 케이스를 조져보도록 합시다.
Priority Inversion (우선 순위의 뒤바뀜)
개념
우선 순위의 뒤바뀜이란
qos (서비스 품질)가 뒤바뀌어서 작업이 진행되는 경우
를 뜻합니다. 어떤 상황인지 그림으로 자세히 보시죠 ✍🏻
이렇게 각각의 qos 가 설정된 세개의 queue가 있습니다. 그리고 각 큐는 할당된 task 를 처리를 위해 각각 Thread 2
, Thread 3
, Thread 4
를 이용한다고 가정합시다.
위에 있는 .userInteractive
가 셋 중 가장 우선 순위가 높고, .defualt
가 가장 낮습니다. (qos가 기억이 안나시는 분들은 [iOS] 차근차근 시작하는 GCD — 5 으로 고고)
우선 순위가 가장 낮은 default queue
에 가장 먼저 일(task 1
)이 들어왔습니다! Thread 4
는 task 1
을 할당 받아 처리를 하는데, 이때 해당 작업은 공유 자원 a
를 필요로 하네요. 중간에 다른 곳에서 동시에 사용하지 못하게 잠금 처리도 해놓습니다. (자원을 배타적으로 사용)
도중에 두번째 우선 순위를 가진 userInitiated queue
에 일(task 2
)이 들어왔습니다. 이전에 실행되고 있던 작업은 우선 순위에 밀려 잠시 멈추게 되고, Thread 3
에서 방금 들어온 task 2
를 실행합니다.
여기서 잠깐! 🙆🏻♀️
방금 글을 읽으면서 “엥..?? 멈춘다고..?? concurrency 로 실행중인데 다른 task가 들어온다고 멈추나..??”라고 생각하신 분들이 계실 수 있어요!
맞습니당. 만약 다른 core (더 정확히는 물리 스레드)에서 thread 를 돌리는 방식으로 concurrency를 획득하고 있다면 (parallelism) 단순히 다른 스레드에 task 가 들어온다고 해서 멈추진 않을거예요.
하지만 동일한 core (더 정확히는 동일한 물리 스레드. 하이퍼스레딩에 의해 하나의 코어에 여러 개의 물리 스레드가 존재할 수 있게 되었으므로)에 여러개의 논리 스레드가 할당되어 돌아가고 있는 상태라면, time slicing 기법에 의해 concurrency를 획득할테고, 다른 스레드의 작업을 위해서는 현재 스레드 작업이 멈추게 될거예요.
현재 예시는 해당 상황을 가정하고 “task가 멈춘다"라는 표현을 사용합니다.
다시 도중에 첫번째 우선 순위를 가진 userInteractive queue
에 일(task 3
)이 들어왔습니다. 이전에 실행되고 있던 작업(task 2
)은 우선 순위에 밀려 잠시 멈추게 되고, Thread 2
에서 방금 들어온 task 3
을 실행합니다.
앗 그런데 task 3
은 공유 자원 a
를 필요로 하네요. 자원 a는 이미 task 1
에 사용되면서 잠겨있는 상태기 때문에 당장 task 3
에서 사용할 수 없습니다. 따라서 task 3
도 잠시 멈추게 되고, task 3
의 우선순위에 밀려 멈춰있던 task 2
가 재개됩니다.
task 2
가 끝나면 막혀있던 task 1
도 재개됩니다.
작업이 끝나면 자원 a 의 사용도 끝나므로 비로소 task 3
이 해당 자원을 점유 후 작업을 진행할 수 있게 됩니다.
결국 작업은 task 2
-> task 1
-> task 3
순으로 완료됩니다. 즉 우선 순위가 가장 높았던 task 3
이 가장 늦게 실행 되는 상황입니다.
이런 상황을 바로 동시성 문제 중 하나인 우선 순위의 뒤바뀜 문제 상황이라고 할 수 있습니다.
우리는 위의 예시를 통해 high priority task가 필요한 자원을 low priority task 가 잠그고 있는 경우 (자원을 배타적으로 사용) 작업의 우선 순위가 바뀔 수 있다는 것을 알아보았습니다.
이 외에도 우선 순위 뒤바뀜 문제가 발생할 수 있는 상황은 다음과 같습니다.
- 시리얼 큐에서 high priority task가 low priority task의 뒤에 보내지는 경우
- Operation에서 high priority task가 low priority task에 의존하는 경우
해결 방법
사실 high priority task가 필요한 자원을 low priority task 가 배타적으로 사용하고 있는 경우에 발생하는 Priority Inversion 문제는 GCD 자체적으로 우선 순위 조정을 통해 문제를 해결합니다. (자원을 점유하고 있는 task의 우선순위를 높여서 처리)
상황을 다시 되짚어 봅시다. 우선 순위가 높은 task 3
이 필요한 자원을 task 1
이 사용하고 있어서 task 3
이 더 이상 진행되지 못하는 상태였습니다.
이러한 상황에서 GCD는 task 1
의 우선 순위를 task 3
과 같은 userInteractive 로 올려서, task 1
이 빠르게 처리될 수 있도록 합니다. 그럼 곧 이어 task 3
도 처리될 수 있으니까요.
그림으로 다시 보자면, GCD에 의해 userInteractive 로 우선 순위가 올라간 task 1
이 가장 빨리 처리되는 것을 알 수 있습니다.
task 1
이 완료되면 바로 task 3
이 실행될 수 있기에, 우선 순위가 조정되지 않았던 이전의 결과보다 task 3
이 빨리 완료됩니다.
마지막으로 task 2
가 이어서 실행됩니다.
결국 작업의 완료 순서는 task 1
-> task 3
-> task 2
가 됩니다. (우선 순위 조정 전: task 2
-> task 1
-> task 3
)
이렇게 GCD 에서 자체적으로 처리하는 것 외에도, 공유 자원을 접근할 때는 동일한 qos 를 사용해야 Priority Inversion의 가능성을 줄일 수 있습니다.
마무리
대박적 대박적… 이 시리즈를 마무리 하다니…………………..
이거 연재한다고 자료를 학부때 만들었던 모든 피피티보다 많이 작성한듯..◠‿◠ 홀리몰뤼~~~
강의에는 Operation에 대한 내용까지 다루고 있지만 저는 애초에 GCD까지가 목표였기 때문에 여기까지 포스팅하는 걸로..훟ㅎㅎ.훟ㅎ.… (절대 귀찮아서 안쓰는거 아님~🙄)
함께해온 여러분들도 수고 많으셨습니당 ㅎㅎ 이 시리즈를 읽어오신 분이 얼마나 될지는 모르겠지만?!!! 🥲… 어차피 제 공부가 목적이었기 때문에… 저는 울지 않아요.. ㅎ
그럼 마지막으로 제가 쓴 수강평과 함께 사라지도록 하겠습니다!~!!
지금까지 차근 차근 시작하는 GCD 시리즈였습니다~~ 감사합니다!