이론 정리/컴퓨터 구조
병렬성과 비동기성
철매존
2024. 7. 28. 01:41
728x90
병렬성과 비동기성
경합 조건이란?
- 2개 혹은 그 이상의 프로그램이 같은 자원에 동시에 접근하고 자원 사용 순서에 따라 결과가 달라지는 경우
공유 자원?
- 사실 대부분이 공유될 수 있다.
- 메모리도 가능하고 프린터 사용도 그렇고 FPGA 등등도...
프로세스와 스레드
- 운영체제는 프로세스를 관리한다.
- 프로세스란, 사용자 공간에서 실행되는 프로그램이다.
- 멀티코어 시스템에서는 여러 프로그램이 병렬로 실행될 수 있다.
- 그래서 이 병렬 프로그램이 자원을 공유하면 경합 조건이 발생할 수 있다.
- 멀티코어 시스템에서는 여러 프로그램이 병렬로 실행될 수 있다.
- 프로세스란, 사용자 공간에서 실행되는 프로그램이다.
- 요즘에는 액티비티 핸들러는 더이상 처음부터 끝까지 한번에 실행이 끝나는 간단한 작업이 아니게 됐다.
- 핸들러를 인터럽트 : 즉 핸들러가 실행하는 중간에 잠시 실행을 중단하면서 상태를 저장하고, 나중에 그 위치로 돌아와 실행을 재개할 수 있다는 뜻.
- 위를 보면 그러면 핸들러가 실행하는 중간에 잠시 실행을 중단하면서 상태를 저장하고 나중에 그 위치로 돌아가야 한다는 것 -> 스택에 저장한다.
- 근데 프로세스마다 스택이 하나뿐이고... 모든 핸들러가 각각 따로 스택을 가져야 하는 것으로 보인다.
- 스레드 : 정적인 데이터와 힙을 공유하지만 자체적으로 스택을 갖는 프로그램의 일부분
- 즉 각각의 스레드는 자신이 CPU레지스터를 완전히 소유한다고 가정한다.
- 한 스레드에서 다른 스레드로 실행이 넘어갈 때는 스레드 스케줄러가 CPU레지스터를 저장해야 한다.
- 스레드는 프로세스보다 컨텍스트 스위칭이 더 빠르고, 그래서 경량 프로세스라고 부르기도 한다.
- 이 스레드는 좋은점도 많은데 남용하면 안좋을 수도 있음
- 데이터를 공유하기 때문에 보안 문제 생김.
- 한 탭에서 문제가 생기거나 버그가 발생하면 전체 프로세스가 멈춘다.
- 어떤 스레드가 작업을 완료하는 데에 시간이 오래 걸리면 다른 모든 곳에서 스레드가 실행되지 못할 수 있다.
- 이 스레드는 좋은점도 많은데 남용하면 안좋을 수도 있음
- 스레드는 프로세스보다 컨텍스트 스위칭이 더 빠르고, 그래서 경량 프로세스라고 부르기도 한다.
- 한 스레드에서 다른 스레드로 실행이 넘어갈 때는 스레드 스케줄러가 CPU레지스터를 저장해야 한다.
- 즉 각각의 스레드는 자신이 CPU레지스터를 완전히 소유한다고 가정한다.
락
- 그래서 여러 작은 연산으로 된 작업을 어떻게 원자적으로 만들 수 있을까?
- 중요한 부분을 mutual하게 원자적으로 해야 한다.
- 이를 어드바이저리 락을 만들어 달성.
- 이게 어드바이저리인 이유는 락을 지킬지 안지킬지를 프로그램이 결정하기 때문.
- 이를 어드바이저리 락을 만들어 달성.
- 중요한 부분을 mutual하게 원자적으로 해야 한다.
- 락을 사용하면 잠금이 걸린 동안 다른 곳에서는 접근이 안된다.
- 근데 이러면 문제가 있는데 만약에 락이 걸어놓은 스레드가 속도가 오래 걸리거나 혹은 죽는다면? 아니면 락을 아예 반환하지 않는다면?
트랜잭션과 작업 크기
- 다음 연산 수행하기 전에는 앞의 연산이 성공했는지를 알아야 하기 때문에 양방향 통신이 되어야 한다.
- 트랜잭션은 결국 그 안에 있는 연산이 모두 성공하거나 실패해야 하는 것이다.
- 근데 이 트랜잭션에 락이 걸리기 때문에 최소한으로 걸 수 있게 해야한다.
- 락을 작은 부분만 잘 거는걸 세밀하다. 큰 범위를 하는걸 성기다 라고 표현함 (근데 이거 자주 쓰는 표현은 맞냐?)
락 대기
- 근데 위의 트랜잭션이 작은 부분만 잘 걸었다고 가정해도, 그 락을 기다리는 동안 다른 일을 할 수 없다면 이득이 없다.
- 근데 락을 기다리는 동안 다른 일을 할 수 없어도 그 락 기다리는 시간 자체가 줄어들면 이득아닌가..? 라고 생각했는데 프로그램의 블로킹 / 논블로킹 이슈라면 이해가 간다.
- 락의 해제를 알 수 있는 스핀락이나 pub-sub 두가지 방식이 있다.
- 블로킹과 논블로킹 방식은 락이 걸린 시간동안 프로그램 실행 여부가 달라진다.
교착 상태
- 위에서 프로그램은 결국 락을 얻을 수 없으면 어찌 됐든 기다려야 한다는 것을 알 수 있다.
- 데드락이 발생하면 두 프로그램은 서로 락을 해제할 수 없게 된다.
- 예를 들어 A와 B가 각각 락을 갖고, 다음 상황에서 서로의 락을 획득하려 하는 경우.
- 서로 이미 락을 걸고 있어그 락을 받기 전까지 이전 락을 해제할 수 없어서 그래도 유지된다.
- 예를 들어 A와 B가 각각 락을 갖고, 다음 상황에서 서로의 락을 획득하려 하는 경우.
- 참고로 데드락은 피하는 방법은 알아서 잘 짜야 한다. 근데 피하기가 진짜 어려운 느낌…
- 이래서 도메인 관련 지식이 진짜 필요한 느낌이기도 하다.
- 데드락 원인과 방지방법
- 4가지 조건이 있고, 공유 자원을 사용하는 프로세스가 이 4개를 동시에 만족하는 경우에만 데드락이 발생한다.
- 상호 배제
- 공유 자원을 함께 사용 불가능하다.
- 점유 대기
- 이미 자원을 점유한 상황에서 다른 자원을 요청한다.
- 비선점
- 프로세스가 할당받은 자원을 강제로 빼앗을 수 없다.
- 순환 대기
- 각 프로세스가 서로 순환적으로 다른 프로세스가 갖고 있는 자원을 요구한다.
- 이 4가지 조건이 모두 맞아 떨어져야 데드락이 발생하기 때문에 하나라도 해결하면 된다.
- 자원을 상호 배제하지 않고 언제든 공유할 수 있는 자원으로 만든다.
- 어느 자원을 점유한 다음에 다른 자원을 요구하지 않고 한꺼번에 자원을 요구한다.
- 선점형으로 바꾼다.
- 자원마다 우선순위를 부여해서 모든 프로세스가 서로 정해진 순서대로만 자원을 요구한다.
- 근데 음.. 뭔가 이거 실제로 가능한 것인지 의문이기는 하다.
단기 락 구현
- 락을 구현하는 방법은 실제로는 하나뿐이지만 프로그램에 표현하는 방법은 여러가지이다.
- 프로세스가 락에 사용하기 위한
test and set(TAS)
명령어를 사용한다.- 이거는 원자적인 명령이다.
- 어떤 메모리 위치에 들어있는 값을 1로 설정하고, 그 위치에 들어있던 값을 돌려준다.
- 처음에 이 메모리에는 0이 들어있어야 한다.
- 둘 이상의 프로세스가 접근하면 한 프로세스가 처음 접근하면 0을 반환하고, 나머지는 1을 반환한다.
- 요렇게 하면 락을 직접 구현해준다.
- 동시에 락을 얻으려는 프로세스가 아주 많다면
compare and swap(CAS)
도 있다.- 위의 방식과 비슷하지만 한 값만 설정하는 대신 명령어를 호출하는 쪽에서 예전 값과 새 값을 모두 호출한다.
- 이게 왜 장점이 있나? 라면 이거를 보면 좋을 것 같다.
- 간단히 말하자면 TAS는 값을 설정하는 연산으로, 성공할 때까지 메모리 쓰기를 시도한다.
- CAS는 이름에서 볼 수 있듯, 비교해보고 일치한 경우에만 값을 변경한다. 일치하지 않으면 아무 작업도 하지 않으니 메모리 쓰기 작업 수가 줄어든다.
브라우저 자바스크립트
보통 자바스크립트에서 동시성을 많이 써볼텐데, 자바스크립트는 단일 스레드다. 뭐지?
- 자바스크립트는 사용자 이벤트에 응답하는 짧은 프로그램을 실행하기 위해 이벤트 루프 모델을 사용한다.
- 실행할 작업을 이벤트 큐에 추가하고 작업을 큐에서 하나씩 꺼내서 실행.
- 단일 스레드이기 때문에 인터럽트할 수 없다.
- 근데 이거 이벤트가 큐에 추가되는 순서도 제어할 수 없다.
- 실행할 작업을 이벤트 큐에 추가하고 작업을 큐에서 하나씩 꺼내서 실행.
- DOM이 추가된 후에 웹 페이지 일부만 변경 가능해졌다.
- 그리고 XHR이 등장하면서 기존의 페이지 로드 모델이 아니라 백그라운드에서 브라우저-서버 간 통신이 가능해졌다.
- 근데 단일 스레드라서 비동기 통신이랑 같이 쓰는게 골때리다.
- 뭔가 순서대로 보낸 후에 응답을 받고, 그 이후에 다른 응답을 보낸게 있다고 치면, A B C 순서로 보냈는데 그 응답은 동일한 순서가 아닐 수 있다. 응답을 통해 이후 순서가 제어된다면 원하는 순서로 작동하지 않을 수도 있다는 뜻.
- 그래서 반드시 응답이 들어와야 작동하는 콜백을 활용할수 있기는 한데, 보기 어렵기도 하고 이해도 어렵다.
비동기 함수와 프로미스
자바스크립트에 프로미스라는 구성요소가 추가됐다.
비동기 콜백 메커니즘을 언어 고유 기능으로 넣어서 라이브러리가 잘못 비동기 연산을 구현하지 못하게 한다.
비동기로 진행되는 함수들이 성공/실패했는지에 따라서 다른 함수가 수행될 수 있도록 한다.
근데 이거 굳이 프로미스 아니고도 충분히 구현 가능할 것 같은데? 싶은데 얘는 체이닝이 가능하다.
then 메서드가 다른 프로미스를 반환하기 때문.