[iOS] 차근차근 시작하는 GCD — 15

우선 순위의 뒤바뀜(Priority Inversion)과 해결 방법에 대해 알아봅시다

naljin
8 min readMay 21, 2021

Previously on GCD…

우리는 [iOS] 차근차근 시작하는 GCD — 11 부터 여러개의 스레드에서 동시에 일을 진행할 때 발생할 수 있는 문제, 즉 동시성 문제(Concurrency Problem)에 대해 다뤄오고 있습니다. 아래 세가지 케이스 중 경쟁 상태, 교착 상태는 모두 알아봤으니

  1. Race Condition (경쟁 상태)
  2. Deadlock (교착상태)
  3. 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 4task 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의 가능성을 줄일 수 있습니다.

마무리

대박적 대박적… 이 시리즈를 마무리 하다니…………………..

이거 연재한다고 자료를 학부때 만들었던 모든 피피티보다 많이 작성한듯..◠‿◠ 홀리몰뤼~~~

109 페이지요..?

강의에는 Operation에 대한 내용까지 다루고 있지만 저는 애초에 GCD까지가 목표였기 때문에 여기까지 포스팅하는 걸로..훟ㅎㅎ.훟ㅎ.… (절대 귀찮아서 안쓰는거 아님~🙄)

함께해온 여러분들도 수고 많으셨습니당 ㅎㅎ 이 시리즈를 읽어오신 분이 얼마나 될지는 모르겠지만?!!! 🥲… 어차피 제 공부가 목적이었기 때문에… 저는 울지 않아요.. ㅎ

그럼 마지막으로 제가 쓴 수강평과 함께 사라지도록 하겠습니다!~!!

좋아보이져?!?! 님들도 수강하셈요~~!!!

지금까지 차근 차근 시작하는 GCD 시리즈였습니다~~ 감사합니다!

이전 포스팅 👈🏻

출처

--

--