멀티스레드 단점 -> 유의사항. -> 멀티스레드 유의사항. 멀티스레드 환경에서 고려되어야 할 점. 프로세스와 스레드 관계도 그림 장점 -> 동시성, 처리 성능 단점 -> 값의 불일치 -> 세마포어를 사용. 교착발생 가능 -> tryLock 멀티스레드 유의사항 테스트 진행. -> 스레드 풀 설명, 테스트 설명, 결론과 항공쪽에서 어떻게 사용할 지. 동기, 비동기 알아야되는 이유 멀티스레드로 인해서 생길 수 있는 문제 여러개를 동시에 실행하기 위해 멀티스레드를 사용. 그럼 여러개의 정보들을 가져와서 빠르게 보여진다. 하지만 동시에 접근하면 값이 의도치 않게 변경될 수 있어서 동기화를 해야 함. 왜냐면 여러 스레드에서 동시에 같은 자원을 변경하기 때문. 이를 해결하기 위해 동시접근을 제한할 만한 무언가가 필요함. 그 무언가가 락이 됨. 락이란 그래서 스레드에서 락을 소유함으로써 동시접근을 제한하도록 만듬. 근데 동기화하면 서로 락을 소유한채로 서로의 자원을 요구할 수 있음. 이러면 무한히 대기하게 됨. 비동기로 논블라킹 여러 스레드에서 준영 스레드 -> 프로세스랑 무슨 차이가 있냐 싱글 스레드 문제점. 멀티 스레드 이점과 문제점 -> 비유 혹은 실제예시. 어디서 쓰일지 사람들이 알법한 예시로. 예약결제같은 (쓰레드 풀 예시.) 좋은 비유. 집 좋다. 쓰레드와 동기/비동기 -> 블락킹, 넌블라킹 빼야되나.. 동기 비동기.. 목차 프로세스와 스레드 관계도 그림 장점 -> 동시성 단점 -> 값의 불일치 -> 세마포어를 사용. 교착발생 가능 -> tryLock 멀티스레드 유의사항 테스트 진행. -> 스레드 풀 설명, 테스트 설명, 결론과 항공쪽에서 어떻게 사용할 지. 동기, 비동기 알아야되는 이유 쓰레드를 알아볼게요. 호텔이 하나 있고 그 호텔에는 방이 여러개가 있습니다. 각 방은 아무나 들어갈 수 없는 프라이빗한 공간이고 오직 방주인만 들어갈 수 있습니다. 이때 호텔을 프로세스, 방을 쓰레드라고 합니다. 호텔에는 모두가 이용할 수 있는 식당이 있고 한사람만 이용할 수 있는 쓰레드를 알아볼게요 주방이 있고 쓰레드 = 스택 + 레지스터 + PC = 메인요리공간 + 메모정보 + 쓰레드를 알아볼게요 쓰레드는 프로세스 내에서 실행되는 흐름의 단위를 의미합니다. 유튜브로 비유해보면, 영상을 다운로드 받으면서 동시에 영상을 볼 수가 있죠? 이렇게 유튜브라는 하나의 프로세스에서는 여러 갈래의 작업이 동시에 진행될 수 있어야 하는데 이 갈래가 바로 스레드입니다. 주방에는 요리사가 있고 각각의 요리를 만드는 도마가 있습니다. 이렇게 조리 공간을 나눠서 요리사 혼자 돌아다니면서 여러개를 하든 여러 요리사가 병렬적으로하든 혹은 이 둘을 섞어서 하든 이 메뉴들을 계속해서 만들어 내려고 하는 것이죠. 여기서 핵심은 도마들끼리는 독립적인 공간이라는 것입니다. 즉 순대국 도마에서 햄버거 도마에 있는 자원을 쉽게 가져올 수 없어요. 이 도마들을 컴퓨터에서는 프로세스, 요리사를 CPU라고 합니다. 그리고 한 요리사가 파스타를 요리하다가 중간에 멈춰서 햄버거를 요리하러 갈 수도 있는데요, 이때 가기전에 PCB라는 메모장에 다음에는 패티를 올려야 할 지 빵을 구어야 할 지에 대한 정보를 적고 갑니다. 그래야 요리사는 다시 왔을 때 작업을 이어갈 수 있을테니까요. 컴퓨터에서는 이 과정을 컨텍스트 스위치라고 합니다. 프로세스들 끼리 전환을 할 때는 PCB라는 프로세스 제어블록에 다음 명령어는 무엇인지, 중간값은 어떻게 되는지와 같은 정보들을 적어놓고 다음 PCB를 불러와서 작업을 실행하는 것입니다. 파스타를 만드는 프로세스에서는 면을 삶는 스레드가 진행되는 동안 소스를 만드는 스레드도 진행될 수 있겠죠. 한 메뉴의 스레드들은 같은 조리대에서 진행됩니다. 물은 파스타 도마에서 끓이고 소스는 파스타 순대국 도마에서 만들면 같은 조리대에서 작업하는 것보다 일하기가 훨씬 힘들겠죠. 이를 컴퓨터 관점에서 본다면 프로세스들은 컴퓨터의 자원을 분할해서 쓰지만 쓰레드는 프로세스 내의 주어진 자원을 함께 공유해서 쓰는 것입니다. 즉 공유자원에 대한 접근이 쉽다라는 것이 스레드의 특징입니다. 이게 공유자원에 쉽게 접근할 수 있게되어 좋은점도 있지만 단점도 있습니다. 프로세스 안에서 공유되는 변수에 스레드 두개가 동시에 손을 대요. 이는 컴퓨터에서 큰 에러가 될 수 있습니다. 어떤 버튼을 누르면 1씩 올라갑니다. 쓰레드 두개에서 이 버튼을 10번씩 누르면 결과물이 20이 나와야하는데 그보다 적은 숫자가 나와버립니다. 두 개가 동시에 누르는 경우에는 숫자가 1만 올라가기 때문입니다. 이처럼 동시에 접근했을 때 위험할 수 있는 영역을 임계영역이라고 해요. 이를 방지하기 위한 장치로 뮤텍스와 세마포어라는 것이 있습니다. 뮤텍스는 쉽게 말해 열쇠를 통해서 1인화장실에 들어가는 것과 같습니다. 열쇠가 있는 사람만 들어갈 수 있고 사람이 들어갔다면 그 사람이 열쇠를 갖고 나올때까지 대기해야겠죠. 이때 열쇠가 컴퓨터에서 뮤텍스입니다. 뮤텍스를 갖고있어야만 임계영역에 접근할 수 있는거죠. 그리고 세마포어는 마치 휴게소 화장실에 있는 전광판처럼 생각할 수 있습니다. 사람들은 이 전광판의 수를 보고 화장실에 들어갈 지 말지를 결정합니다. 전광판의 수가 0보다 크다면 들어가고, 0이라면 들어가지 않습니다. 컴퓨터에서 이 전광판이 세마포어를 의미하고 이를 통해 임계영역에 대한 접근을 제한합니다. 자 두개의 장치를 통해서 값을 안전하게 다룰 수 있게 되었습니다. 그런데 기껏 이런 장치들로 동시접근을 막았는데 이 장치들로 인해 또 다른 문제가 일어납니다. A는 노트를 갖고있고 B는 연필을 갖고있습니다. 그 상태에서 A가 노트에 글 좀 쓰게 연필을 달라고합니다. 그와 동시에 B도 A에게 나도 노트에 글 좀 쓰게 연필을 달라고 합니다. 둘 다 자원을 가진채로 서로 요구하니까 무한히 대기하게 되는 것입니다. 이를 교착상태라고 합니다. 실제로 운영중인 서버에서 교착상태가 일어나면 그 서비스가 중단되는 것이기 때문에 서버를 재기동해야하는 경우가 생깁니다. 우리 항공엔진에서도 가끔 이런 이슈가 발생해서 재기동하는 것으로 알고 있습니다. 이를 해결하기 위해서 여러가지 전략이 있을 수 있는데요, 저는 락을 걸 때 타임아웃을 정하는 방식을 소개드리겠습니다. 락 타임아웃 방법은 자원을 무한정 기다리는 것이 아니라 특정시간동안만 기다리다가 락을 소유하지 못하면 요청을 취소하는 것을 말합니다. 예를 들어 A가 1초 락타임아웃을 걸면 1초간 기다리다가 B가 연필을 내려놓지 않는다면 그 요청을 포기하게 됩니다. 이 락타임요청의 핵심은 교착상태가 일어났을 때 무한히 대기하지않고 특정시간동안만 대기하다가 포기하여 결국 누군가는 자원을 갖도록 하는 것입니다. 아래 코드는 락타임요청을 하고 난 후에 실패하면 락을 전부 반환하고 시간이 지난 뒤에 다시 재요청하는 전략예시인데요, 락을 소유하고 있는 스레드가 있다면 1초동안 대기하는데 1초가 지났는데도 락을 소유하지 못하면 else문으로 가서 갖고 있던 락을 반환합니다. 그래서 다른 스레드에서 자원을 다 가져서 작업할 수 있도록 만들고 이 스레드의 작업이 다 끝난다면 다시 작업을 재요청해서 전체 시스템에 문제가 없도록 만드는 방법입니다. ```java // 스레드 작업 내부 note.lock() // 락을 소유한 스레드가 없다면 진입 if (pencil.tryLock(1)) // 공유자원 접근 else // 락 소유 실패. note.unlock() // 재요청 ``` 결론적으로 교착상태를 해결할 수 있는 방법이지만 시간을 얼마나 설정하냐에 따라서 시스템 성능에 문제가 생길 수도 있기 때문에 시스템의 요구사항에 따라서 적절하게 조절할 수 있어야 합니다. 다음은 동기/비동기입니다. 햄버거를 만드는 사람이 있고 파스타를 만드는 사람이 있습니다. 햄버거를 만드는 사람은 패티가 구워지는 동안 계속 기다리고 있고 파스타를 만드는 사람은 물을 끓이면서 소스를 만들고 있습니다. 이때 햄버거를 만드는 사람은 동기적으로 일하고 있는거고 파스타를 만드는 사람은 비동기적으로 일하고 있는 겁니다. 그런데 물을 끓이는 동안 소스를 만드는 게 뭔가 익숙하죠? 네 쓰레드입니다. 물을 끓이는 건 소스를 만드는 것과는 다른 쓰레드에서 이뤄지는 겁니다. 동기와 비동기를 정의하면 다음과 같습니다. 동기: 다음작업이 이전작업을 기다리는 것. 비동기: 다음작업이 이전작업을 기다리지 않는 것. 제가 동기와 비동기의 차이를 간단한 앱으로 만들어보았는데요, 먼저 동기방식으로 사진을 로딩하면서 스크롤하는 경우에 상당히 느린 것을 볼 수 있습니다. 이는 메인스레드라는 하나의 스레드에서 동기적으로 사진을 불러온 뒤에 스크롤을 내리거나, 스크롤을 내린뒤에 사진을 불러오기 때문입니다. 이를 비동기방식으로 구현한다면 다른 스레드에서 이미지를 불러오고 실제 적용을 메인스레드에서 하고있기 때문에 반응성이 빠른 것을 볼 수 있습니다. 이렇게 비동기는 성능과도 깊은 연관이 있습니다. 이 비동기 방식의 예시는 실제로 여러 곳에서 찾아볼 수 있습니다. 웹툰에서 스크롤을 내리면서 중간중간에 컷을 보여준다거나 파일을 다운로드 시켜놓고 웹 서핑을하는 것이 그 예시가 될 수 있습니다. 파일이 언제 다운로드 되든지 관계없이 자신의 일을 계속 진행하는 것이죠. 여기서 핵심은 비동기는 결과값을 기다리지 않는 것입니다. 이러한 특징으로 인해 여러 작업들을 동시에 수행하거나 오래 걸리는 일을 할 때 사용합니다. 반대로 동기적인 예시는 로그인이 있습니다. 아이디, 패스워드를 입력하고 로그인버튼을 누르면 로그인이 성공하거나 실패할 때까지 사용자는 아무것도하지않고 대기합니다. 그리고 인증 토큰이라는 결과값을 받아서 다음일을 진행합니다. 동기/비동기 정리 동기: 흐름이 순차적으로 이루어져야 할 때. 비동기: 각자 독립적이지만 유사한 여러개의 작업을 처리할때, 네트워크 호출과 같이 시간이 걸리는 작업이 필요할 때. 동시적으로 작업할 수 있다고 무작정 멀티스레드를 이용하기 보다는 현재 개발하려고 하는 서비스의 특징을 잘 이해하는 것이 중요합니다. 속도를 조금 줄이더라도 안정성을 ## Q&A 1. 프로세스가 뭐지? SSD나 하드디스크와 같은 저장공간에 있는 프로그램을 운영체제로부터 자원을 할당받아 메모리에 올리는 것을 말합니다. 2. 그럼 프로세스도 실행할 수 있는거아닌가? 실제로 실행되는 단위는 쓰레드입니다. 이 때문에 프로세스는 최소 1개 이상의 메인쓰레드를 갖고 있습니다. 3. 작업이 쓰레드라고 했는데 너무 추상적인 것 아닌가? 쓰레드는 실제로 무언가 작업을 할 수 있는 공간이자 실행될 수 있는 것으로 보면 좋겠습니다. 4. 요리사 한명 왔다갔다하는거랑 여러명이랑 합쳐서 하는게 여러명이서 하는 거랑 차이가 뭐죠? 한명이 왔다갔다 요리를 하는 것은 동시적으로 요리하는 것처럼 보일 수 있고 여러명이 만드는 것은 실제로 동시에 일을하는 병렬적으로 하는 것입니다. 그리고 이 두 개를 합치면 여러명이 일을 실제로 동시에하면서 조리대도 왔다갔다 하는 것이죠. 이는 CPU의 코어가 여러개가 있을 때로 비유할 수 있습니다. 둘 다 합친것이 CPU를 더 많이 일하게 할 것입니다. 5. 프로세스끼리 메모리 침범이 불가능한가요 그럼? 프로세스끼리도 IPC라는 방법으로 메모리를 공유할 수는 있습니다. 커널이라고 하는 운영체제 서비스를 대행해주는 매개체를 통해 각 프로세스에 공유되는 메모리 영역을 할 수는 있습니다. 하지만 애초에 설계될 때 분리되어서 나왔고 메모리를 공유하기 위해 추가적인 메모리 공간을 만들어줘야하기 때문에 조금 복잡합니다. 6. 근데 컴퓨터에서 프로세스는 실행할 수 있던데 그럼 CPU는 프로세스를 실행시키는 거에요? 정확히는 프로세스 안의 스레드를 실행시킵니다. 앞에서 스레드는 실행가능한 단위라고 말씀드렸잖아요? 이 때문에 하나의 프로세스는 메인스레드라는 최소 1개의 스레드를 가져야 합니다. 7. Context Switching이 CPU를 점유하는 프로세스를 다른 프로세스로 변경하는 거라고 말했는데 그 사이에 PCB에 값을 저장하잖아요. 이거말고 다른 작업도 있나요? 프로세스 끼리의 전환이 일어날 때는 PCB를 백업하는 작업 뿐만 아니라 분리된 영역이기 때문에 메모리 관련된 부분도 초기화를 해주어야 합니다. 예를 들어 CPU의 캐시메모리를 비워줘야 이전 프로세스의 데이터를 참조하지 않게 됩니다. 그리고 CPU는 프로세스에 접근할 때 가상메모리를 통해서 접근하는데 이 가상메모리 주소를 실제 메모리 주소로 변환시키는 작업이 필요합니다. 이 과정을 메모리 관리유닛이라는 MMU가 하기에 Context Switching을 할 때 이 MMU가 바라보고있는 주소체계 또한 바꿔줘야 새로운 프로세스에 접근이 가능해집니다. 8. 파스타 조리대에서 순대국 조리대의 소면을 가져오면 어떤 일이 일어나나요? 컴퓨터에서는 다른 프로세스로부터 데이터를 가져올 수는 있습니다. IPC라는 방법으로 커널이라는 운영체제 서비스를 대행해주는 매개체를 통해 가져올 수는 있어요. 커널에게 공유메모리 공간을 할당받아서 진행이 가능합니다. 9. 지금이게 스레드가 여러개고 프로세스도 여러개인데 그럼 이걸 뭐라하나요? 컴퓨터에서 이를 멀티 프로세싱이라고 합니다. 이 방식으로 서비스를 구현한다면 자기 컴퓨터의 코어수만큼 이 작업들이 할당돼서 동시에 진행되기 때문에 굉장히 빠르게 일을 처리할 수 있습니다. 10. trylock은 무엇이죠? 자바의 ReenterantLock이라는 클래스에서 지원하는 메서드입니다. 1초동안 락을 기다린다 라는 뜻을 갖고 있습니다. -> 10-1. 그럼 이 클래스가 있는 이유가뭐지? -> 기존의 뮤텍스에서 락을 기다리거나 현재 스레드가 락을 얻었는지와 같은 추가적인 메서드 지원을 통해 좀 더 폭넓게 교착상태 전략을 세울 수 있습니다. 11. 동기의 정의가 잘못됐다. 동시에 시작하거나 동시에 끝나는 것도 동시인데 맞습니다. 동기의 뜻은 동시에 시작하거나 동시에 끝나거나 아니면 순차적으로 진행되는 것인데 저는 순차적인 경우의 동기를 말하려고 했습니다. 12. 의존성이 있는데 비동기로 해야할 경우는 언제인가? 의존성으로 인해 대기하고 있는 스레드가 몇개인지에 따라서 동기방식으로 통신할 지 비동기방식으로 통신할 지 결정하는 것이 좋을 것 같습니다.ㅡㅡ 13. 비동기통신이 무조건 반응성이 좋나? 사용자입장에서 동시에 작업을 수행할 수 있기 때문에 반응성이 좋다고 생각합니다. 14. 비동기 호출이 무조건 응답시간이 단축되나? 일반적으로는 그렇습니다. 하지만 비동기 방식은 멀티스레드를 이용해서 하는 방식이기 때문에 특정 스레드가 다른 스레드에서 필요한 자원을 갖고있다면 많은 시간 대기해야 할 수 있습니다. 이경우 동기방식과 큰 차이가 없을 수 있습니다. 15. 메인쓰레드와 쓰레드의 차이는? 메인쓰레드는 프로그램이 시작하면 가장 먼저 실행되는 쓰레드이고 다른 쓰레드들은 이 메인쓰레드로부터 파생돼서 만들어지는 쓰레드입니다. 다음으로는 비동기 방식을 홈페이지에서 어떻게 활용하면 좋을까를 생각해봤습니다. 고객이 검색칸에 3명이 세부에 주말을 포함해서 3박4일 다녀오고 싶어. 라고 적는다면 자연어 처리 AI가 이를 분석해서 항공, 호텔, 액티비티 API를 보내는 요청 모델에 매핑합니다. 그리고 이를 순차적으로 호출하는 것이 아니라 비동기방식으로 동시적으로 호출함으로써 전체 응답시간을 단축시키는 경우에 사용하면 좋을 것 같습니다.