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

교착 상태(Deadlock)와 해결 방법에 대해 알아봅시다

Previously on GCD…

개-하! 생각보다 빨리 다음 GCD 시리즈로 돌아왔습니다. 이번 시간은 내용이 짧기 때문이져 ㅎㅎ ㅎㅎㅎㅎ

우리는 [iOS] 차근차근 시작하는 GCD — 11 부터 여러개의 스레드에서 동시에 일을 진행할 때 발생할 수 있는 문제, 즉 동시성 문제(Concurrency Problem)에 대해 다뤄왔어요.

동시성 문제는 크게 아래의 세가지 케이스가 있는데

  1. Race Condition (경쟁 상태)
  2. Deadlock (교착상태)
  3. Priority Inversion (우선 순위의 뒤바뀜)

이 중 경쟁 상태에 대해 GCD 시리즈 11, 12, 13에 걸쳐 알아본것이죠(개념, 해결 방법 등)

이제 드디어 넘어갑니다… 바로 바로 동시성 문제 두번째!!! 교착 상태로!!!!

Deadlock (교착 상태)

개념

데드락이란

프로세스가 자원을 얻지 못해 다음 처리를 하지 못하는 상황

을 뜻합니다.

한정된 자원을 여러 곳에서 사용하려고 할 때, 자원을 얻지 못해 다음 처리가 어려운 상태이죠.

예를 들어 스레드 2모리 A 를 점유해 사용하면서 경쟁 상황을 방지하기 위해 내부적으로 잠금 처리를 해놨다고 합시다.

그리고 메모리 B를 동시에 사용하려고 하져

오잉 그런데 이미 메모리 B는 다른 스레드(스레드 3)에서 사용 중이라 잠겨 있는 상태입니다. 그럼 스레드 3의 동작이 끝날 때까지 기다리면 메모리 B를 사용할 수 있겠져?

앗 근데 스레드 3메모리 A를 요청하고 있는 상태였네요! 메모리 A스레드 2에서 이미 사용하고 있었기 때문에, 하염없이 메모리 A를 점유하고 있는 동작이 끝나기만을 기다리고 있었을 거예요

홀뤼 몰뤼~~~ 결국 서로 필요한 자원 얻지 못하기 때문에 이러지도 저러지도 못한채로 일의 진행이 멈춰버리겠군여! 이것이 바로 데드락입니다.

예시

데드락 발생 상황은 여러 개의 세마포어가 존재할 때 순서를 잘못 설계한다든지… 등등이 있겠지만 사실 감이 잘 안오쥬?

하지만 hoxy 그거 아시나요.. 저희는 이미 데드락 예시에 대해 다뤘었다는 사실..

자자 저 포스팅에서 “현재와 같은 큐에 sync로 작업을 보내면 안된다 로 검색해서 보고 오세여

ㅋㅎ 사실 저도 기억이 잘 안나니까 다시 같이 살펴봅시당.

같은 큐에 동기적으로 작업을 보낸다는건 바로 이런 코드인데요, 보다시피 global queue 안에서 같은 global queue에 sync 로 작업을 보내고 있죠?

해당 상황을 그림으로 보면 Thread 2 에 우선 Task A 가 할당 되고, 그 안에서 다시 Task B를 global queue로 보내는 상황임을 알 수 있어요

Task Bsync 로 보냈기 때문에, 해당 작업이 다른 스레드에 할당되어 끝날때까지 Thread 2는 다음 코드를 실행하지 않고 대기하게 됩니다.

근데 만약 global queue에 들어간 Task BThread 2에 할당된다면요?! (같은 큐에 보내면 같은 스레드에 배치될 수 있음)

  • 이미 Thread 2sync 로 인해 멈춰있는 상황이기 때문에, Task B는 더 이상 어떤 작업도 수행할 수 없습니다.
  • Task B가 완료되지 않기 때문에 sync로 보낸 작업이 끝나길 기다리고 있는 Thread 2도 더 이상 작업을 진행 할 수 없습니다.

즉, 데드락이 발생합니다.

글을 다 복붙해오기에는 좀 긴 분량이라 간략히 설명하겠스요. 자세한 설명을 보고 싶다면 꼭 위의 포스팅을 확인하고 오시라요!

튼 이렇게 데드락이 발생 가능한 상황으로 ‘현재와 같은 큐에 sync로 작업을 보내는 경우’의 예시를 들 수 있습니다.

이와 비슷하게 메인 스레드에서 DispatchQueue.main.sync {} 를 호출하는 경우가 데드락이 무조건 발생하는 상황에 해당합니다.

해결 방법

데드락은 주로 2개 이상의 스레드를 쓰는 상황에서 발생합니다. 따라서 일반적인 경우, 교착 상태의 단순한 처리 방법으로는 serial queue를 이용해서 해결할 수 있습니다.

왜냐?! 시리얼 큐로 보내진 작업들은 순차적으로 실행되기 때문입니다. 한번에 하나의 스레드에서만 (필요시) 여러 자원에 접근하고, 공유 자원에 여러 스레드에서 동시에 접근하는 상황은 발생하지 않습니다.

하지만 위에서 살펴본 ‘현재와 같은 큐에 sync로 작업을 보내는 작업’ 등은 애초에 설계 상의 오류로 데드락이 발생하는 경우입니다. 즉, Serial Queue 사용으로 해결할 수 있는 케이스에 해당하지 않습니다. (메인 스레드에서 DispatchQueue.main.sync {} 를 호출할 때 사용하는 큐가 Main Queue, 즉 Serial Queue 인데도 데드락이 발생하잖아요?!)

따라서 데드락의 개념을 알고 유의해서 설계하는 것도 중요합니다. 또한 세마포어 같은 한정된 자원을 사용할 때도 순서에 유의해서 사용해야 합니다. 요 정도만 잘 지킨다면 데드락은 잘 발생하지 않는다네요

마무리

후하 후하 정말 GCD의 끝이 다가오고 있군여!!!

다음 시간에는 동시성 문제 중 마지막 케이스인 Priority Inversion 을 주제로 돌아오도록 하겄습니다!!!

백만년전 초안만 작성해 놓은 그것.. 언제쯤 쓸 수 있을 것인가!

허허.. 기약 없는 약속을 하며… 전 0100111000100000..!!

이전 포스팅 👈🏻

다음 포스팅 👉🏻

출처

 https://github.com/sujinnaljin/TIL