일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 클린아키텍쳐
- coordinateSpace
- conflicting access to memory
- RIBs Tutorial
- windowScene
- rib
- iOS Target
- Large File Storage
- Dispatch.main.sync
- 메모리 접근 충돌
- Dependency Rule
- Apple Certificate
- swiftdocs
- App Signing
- memory safety
- ios
- Git Large File Storage
- SWIFT
- in-out
- RxCocoa
- Github file size
- UICoordinateSpace
- Automatically manage signing
- 잡초가득블로그
- 대머리깃허브
- 로버트마틴형
- Ribs
- Concurrent
- RxSwift
- iOS 버전 점유율
- Today
- Total
빙수왕의 개발일지
DispatchQueue.main.sync가 죽는 이유 본문
아래 코드를 그냥 쓰면 앱이 죽는다. 그냥이라는게 맨바닥에 쓴다고 해야하나..? 아무튼 개발자가 추가한 임의의 다른 시점이 아닌, 순서대로 실행되는 앱의 기본 시점에서 쓰면 죽는다.
왜 죽는걸까?
일단 DispatchQueue class에는 대표적인 2가지 인스턴스 메소드가 있다. sync와 async이다.
sync는 메소드를 부른 객체의 시점을 멈추게 해서, 이 객체(DispatchQueue)가 sync안의 블럭을 모두 수행할 때 까지 다른것은 아무것도 할 수 없는 상태가 된다. 이걸 큐를 block 한다고들 한다.
async는 블럭 안의 작업을 하긴 하는데 이 작업이 수행되는동안 다른것이 수행되는걸 막지 않는다. 큐가 다른행동도 할 수 있다. Serial큐에서 이렇게 해줘도 어차피 한번에 1개의 작업만 할 수 있으므로.. 별다른 의미는 없겠지만, 중요한 것은 큐가 block되지는 않는다는 것이다.
그럼 이제 다시 돌아가서, main.sync를 하면 왜 죽을까?
일단 공식 문서에는 deadlock을 초래할 수 있으니 이렇게 사용하지 말라고 한다.
그럼 deadlock은 무엇일까?
운영체제에서 나오는 개념인데, 한정된 자원을 여러 작업들이 사용하려고 하다보니 자원의 반납을 기다리거나 / 자원을 사용하고 반납하는 과정이 이루어져야 하는데, 이게 꼬여서 모든 작업들이 반납을 무한정으로 기다리게 되는 현상이다. (원인은 다양하다)
아래 코드를 보자.
DispatchQueue로 큐를 만들면, 따로 옵션을 지정하지 않으면 serial queue다.
나는 2개의 serial queue를 만들었다.
이걸 실행하면 맨 처음 main queue의 예제와 동일하게 죽는다.
이 상황을 설명하면,
일단 queue1은 serial이므로 한번에 한가지 작업만 수행될 수 있다.
그래서 처음에 queue1.sync를 하면 sync 함수의 전달인자 클로저(A작업이라고 하자)가 끝나기 전까지는 queue1은 블락된다.
그리고 전달인자 클로저 안에서 또 queue1.sync를 불렀다. 이 안의 작업을 B작업이라고 하자.
여기서 문제가 발생하는데, queue1은 이미 A작업이 차지했고 블락상태인데 B작업이 이를 차지하고 싶어한다.
그래서 B작업은 queue1을 본인이 블락하고자 시도한다. B가 차지하려고 시도하는것도 코드상으로 A작업의 일환이므로 시도가 되긴 된다. 그래서 B가 차지하려는 작업을 진행하려 하지만, queue1은 serial이고 한개의 작업만 실행할 수 있다. 그래서 A가 실행되고 있는 자리에 B가 들어갈 수는 없다. 그래서 B는 평생 실행될 수가 없고, 그럼 A역시 평생 하던일을 이어갈 수가 없어진다.
그럼 A와 B 모두 아무것도 못하고 기다리게 되는데, 이걸 데드락이라고 한다.
데드락이 발생했을 때 내 뇌로는 둘다 그냥 기다려야 맞지 않나, 앱이 죽는게 아니고 그냥 멈춰야되지 않나 싶은데 죽는 이유는 애플에서 뭔가 처리를 해둔게 아닐까(??) 이건 나중에 알아봐야겠다.
위의 예제를 내가 좋아하는 게임으로 예를 들어보자면..
내가 AI와 롤 1:1을 한다고 가정하자. AI와 1:1을 하므로 다른사람은 방에 들어올 수 없다. - serial
그런데 게임이 좀 멍청해서 내가 게임을 하는 동안, 다른 사람이 방에 들어오려고 시도를 할 수 있다.
시도를 하면 게임에서는 누군가가 들어오려고 시도한다는 팝업을 띄워주는데, 팝업을 띄우는 동안은 게임이 멈춘다. (팝업을 띄우면서 게임을 진행할 수는 없다. - sync)
그럼 이제 내가 게임을 즐겁게 하고있는데, 어떤 나쁜놈 H가 내 방에 들어오려고 했다. 그러면 게임에서는 팝업을 띄우고 게임은 멈추게 된다. 나는 팝업이 사라져야 게임을 게속할 수 있지만 팝업에는 X버튼 따위는 없고 나에게는 선택권이 없다. 팝업이 사라지기를 기다린다..
H가 방에 들어가야 내 팝업이 사라지겠지만, H는 정원이 1명뿐인 방에 들어갈 수 있을리 없다. (내가 차지했으므로) H도 결국 못들어가고 무제한으로 기다리게 된다.
이제 serial 큐의 sync 작업 안에 serial큐의 sync 작업을 또 부르게 되면 deadlock이 생기기 때문에, 앱이 죽는다는 것을 알았다.
다시 main.sync로 돌아가서, main.sync는 맨바닥에 했는데 왜 죽을까?
여기부턴 내멋대로 이해하기 쉽게 풀어쓴 뇌피셜인데,,(100% 믿지마시오)
우리 눈에 보이지는 않지만, 우리가 쓰는 코드가 커~다란 DispatchQueue.main.sync 안에 들어가있다고 생각하면 이해가 쉬워진다. 애초에 우리가 쓰는 코드는 디폴트로 main queue에서 실행이 된다. 그리고 따로 작업을 하지 않아도 sync하게 코드를 작성한 순서대로 실행이 된다.
그래서 우린 그냥 DispatchQueue.main.sync를 한 번 불렀을 뿐이지만, 사실은 위의 예제처럼 이미 돌아가고 있는 main.sync 안에서 또 부른게 되는거다. 그러니까 데드락에 빠지는게 아닐까 싶다.
'개발 > iOS' 카테고리의 다른 글
iOS) UIButton Custom해서 CheckBox 만들기 (1) | 2021.09.23 |
---|---|
ARC in Swift: Basics and beyond (0) | 2021.09.01 |
앱만들기 #2 - 스토리보드 삭제하기 (0) | 2021.08.15 |
앱만들기 #1 - iOS Deployment Target 정하기 (0) | 2021.08.15 |
Memory Safety - #1 Conflicting Access to Memory (0) | 2021.08.05 |