동시성 이슈는 결국 "공유자원" 으로 인해 발생한다고 보면 될 것 같습니다.
공유자원이란
그렇다면, "공유자원" 은 무엇일까요?
정말 단어 그대로 순수하게 자원을 공유한다고 보면 될 것같습니다.
서버와 클라이언트를 본다면, DB 의 데이터가 공유자원이 될수 있고,
프로세스 간에는 IPC 통신을 하면서 여러프로세스가 동일한 자원에 접근하는 경우에 해당 자원이 공유자원이 될 수 있고,
쓰레드간에는 여러쓰레드가 프로세스 힙메모리 등에 있는 자원에 접근하는 경우에 해당 자원이 공유자원이 될 수 있습니다.
단, 변하지 않는 Read Only 의 자원에 대해서는 동시성 이슈가 발생하지 않습니다.
그러나, 수정이 일어나는 자원에 대해서는 굉장히 조심해야 합니다.
실생활 예시
친구들과 팀프로젝트를 진행중입니다. 파일을 공유하기 위해 구글드라이브 사용하고 있네요.
팀장만 파일을 등록/삭제/수정 이 가능하다면, 팀원들이 받는 파일은 항상 팀장이 마지막으로 수정한 파일일 것입니다.
그러나 모두가 등록/삭제/수정 이 가능하다면?
0.팀원A와 팀원B가 파일을 각각 다운받았습니다.
1.팀원A가 파일에 1이라고 적혀있는 것을 0으로 고쳤습니다.
2.팀원B가 파일에 1이라고 적혀있는 것을 2로 고쳤습니다.
3.팀원A는 파일을 저장하였습니다.
4.팀원B도 파일을 저장하였습니다.
팀원A가 고친 값은 공중으로 사라지게 됩니다.
또다른 예를 볼까요?
0.팀원A와 B가 파일을 각각 다운받았습니다.
1. 팀원A는 발표횟수가 0이라고 되어있는 파일을 가지고 발표를 진행합니다.
2. 발표를 끝낸 팀원A는 발표횟수를 1로 올려두고 파일을 저장합니다.
3. 팀원B는 발표횟수가 0이라고 되어있는 파일을 가지고 발표를 진행합니다.
4. 발표를 끝낸 팀원B는 발표횟수를 1로 올려두고 파일을 저장합니다.
어라? 발표는 분명 2번 일어났는데, 최종적으로 발표는 1번만 한것이 됩니다.
Android Thread 에서 발생하는 동시성 이슈
Android 를 예시로 들겠습니다.
안드로이드 프로젝트를 처음 동작시키면, UI Thread 와 Main Thread 가 존재합니다.
UI Thread 에서는 정말 UI만 그리는 작업을 진행합니다.
Main Thread 에서는 UI를 그리는데 필요한 데이터를 처리하는 작업을 진행합니다.
만약 Main Thread 에서 아직 처리되지 않아서 null 인데이터를 UI Thread 가 그리려고 하면 NullPointException 이 발생할 것입니다.
UI를 그리기 위해 필요한 Data 는 "공유자원" 에 해당합니다.
이처럼 당장 눈에 Thread 라는 것을 정의하지 않더라도, 자신이 사용하는 프레임워크라던가, 동작의 원리등에서 발생할 수 있는 "동시성 이슈" 는 항상 고민되어야 합니다.
Android API 동시성 이슈
간단한 API 를 단계별로 호출하고, 결과에 따라서 UI 를 update 하는 경우에는 크게 접하지 못했을 수도 있습니다.
그러나, 한 화면에 여러개의 API 가 호출되고, 여러개의 API 결과에 따라 UI 가 update 되는 경우에는 이런 문제가 발생할 수도 있습니다.
예를 하나 들어보겠습니다.
API 요청결과는 SUCCESS, FAIL 두가지로만 분류된다고 가정하겠습니다.
SUCCESS 인 경우에는 UI update 에 필요한 데이터가 null 인 경우는 없다고 가정하겠습니다.
또한 FAIL 의 경우에는 재시도 없이 FAIL UI 를 보여준다고 가정하겠습니다.
한개 API
1. API 요청 및 응답대기
2. SUCCESS : 필요한 데이터로 UI Update
2. FAIL : FAIL UI 표시
API 결과가 무엇이든, 동작에 문제는 없습니다.
여러 API
1. 첫번째 API 요청 및 응답대기
2. 두번째 API 요청 및 응답대기
3. 첫번째 API SUCCESS : 필요한 데이터로 UI Update // 두번째 API 결과로 얻는 Data 가 null 이라서 FatalError 발생
3. 첫번째 API FAIL : FAIL UI 표시
4. 두번째 API SUCCESS : FAIL UI 에서 SUCCESS UI 로 변경을 시도, // 첫번째 API 결과로 얻은 Data 가 null 이라서 FatalError 발생
5. 두번째 API FAIL : FAIL UI 표시
운좋게(?) 두개의 API 가 모두 FAIL 인 경우에는 FAIL UI 를 표시하면 되기 때문에 의도대로 동작한다고 볼수도 있습니다.
그러나, 한개의 API 라도 SUCCESS 가 일어나는 순간 저 로직은 FatalError 가 발생합니다.
해결과정을 알아볼까요?
Null 이 아닌 경우에만 해당 데이터가 사용되는 UI 를 그리라는 것입니다.
그러면 최소한 FatalError 에서는 벗어나겠지요?
그런데.. 저게 정말 맞는 해결방법일까요?
정답은. 때에 따라서 맞을수도, 틀릴수도 있다 입니다.
만약 정말 보이고자 하는 의도가 NULL 인 경우에 공백 등으로 UI 표시하는 것이 목적이라면, 저것은 맞다고 할 수 있습니다.
그러나, 의도된 UI or 다음 동작을 위해서 2개의 API 의 결과가 모두 필요한 경우라면, 저것은 틀리다고 할 수 있습니다.
만약 2개 API 의 결과가 필요하다면, 2개 API 의 반환된 결과에 따라서 처리로직이 구현되어야 합니다.
<첫번째 방법>
1. 첫번째 API 요청 및 응답대기
2. 첫번째 API SUCCESS 로 인해 대기
3. 두번째 API 요청 및 응답대기
4. 두번째 API SUCCESS 로 인해 UI Update
또는 첫번째 API FAIL or 두번째 API FAIL 의 경우에는 추가 요청없이 FAIL UI 표시
의 방법이 있겠네요. 이것은 여러 API 를 "순차적" 으로 사용하여 해결하는 방법입니다.
그러나, API 가 10개라면? 20개라면? 100개라면?
연관된 API 가 늘어날수록 위와같은 방법은 코드의 길이와 예외의 상황을 더이상 기억할수 없게 만들어버립니다.
<두번째 방법>
그럼 다음 방법을 살펴볼까요?
0. UI update 를 판단하는 쓰레드 동작
1. 첫번째 API 요청 및 응답대기
2. 두번째 API 요청 및 응답대기
3. 세번째 API 요청 및 응답대기
4. API 요청 결과가 모두 SUCCESS 인 경우 : 0번의 쓰레드의 SuccessCount = 3; 으로 UI Update
4. API 요청 결과가 부분 SUCCESS 인 경우 : 0번의 쓰레드의 SuccessCount < 3 && FailCount > 0 이므로 FAIL UI 표시
두번째 방법은 실제 사용시 Observer 를 사용한다거나, 편리하게 Rx를 사용한 API 콜을 처리하는 등으로 나아가게 됩니다.
이러한 과정은 결국 "동시성 이슈" 로 인해 나온 것이며, "왜" 필요한지 알게 되셨길 바랍니다.
'BackUp (관리중지) > CS 학습' 카테고리의 다른 글
Java GC (0) | 2021.04.28 |
---|---|
GC ( Garbage Collection ) (0) | 2021.04.28 |
쓰레드(Thread) / 프로세스(Process) (0) | 2021.04.27 |
동기(Synchronous) / 비동기(Asynchronous) (2) | 2021.04.27 |
HTTP (HyperText Transfer Protocol) (0) | 2021.04.26 |