Server programming

02_06_멀티쓰레드 프로그래밍_Lock 구현 이론

devRiripong 2022. 8. 3.
반응형
Q1. 락이 걸렸을 때 정책 3가지의 장단점을 설명해 보세요.
Q2. 컨텍스트 스위칭이 일어나는 과정을 설명하세요.
 
앞으로 할 것
Lock을 표준에서 제공하는 mutex만을 사용하는게 아니 실질적으로 우리만의 방법으로 Lock을 구현해 보는 연습을 해볼거야.
 
왜 하냐?
서버 개발을 할 때 평생 달고 다닐 정도로 Lock이 중요하기 때문
면접에서 자주 나오는 개념들이 많이 들어간다.
멀티스레드 프로그래밍을 할 때 도움이 많이 되는 내용들이다.
 
 
그래서 돌고 돌아서 우리가 하고 싶은건?
 
자물쇠를 만들어서 안에 누가 들어가 있으면 한번에 한 명만 들어갈 수 있는 상황을 만들어 줘야 하기에 대기를 해야 하는데 여기서 여러 생각할 점이 있어.
밖에 있는 애 기준으로 안에 누군가가 안에 있다고 가정을 할 때 어떤 정책을 취할 것이냐가 문제야.
비행기를 탈 때 화장실에 누군가가 있다면 어떻게 할지 머리가 돌아가야해. 곧이 곧대로 대기를 하는 방법도 있지만 자리로 돌아가서 3~4분 기다렸다가 다시 가봐도 되고, 여러 방법이 있어. 이런 방법에 따라 성능이 미세하게 달라진다.
 
1. 그냥 무작정 기다린다 (존버 메타)
문 앞에서 대기타는게 효율적일 수도 있어.
단접은 지루해. 혹시라도 안에 있는 사람이 안나오면 시간을 낭비하게 된다.
spin lock에 해당하는 방법이다.
면접에 자주 나오는 질문이다.
 
 
2. 일단 자리로 돌아가고 나중에 다시 오겠다는 방법
단점은 확실성이 없고 운빨에 맞긴다는 단점이 있어.
만약 10초만 기다렸으면 차지할 수 있었는데 그 새 다른 샤람이 들어가는 상황 발생할 수 있어.
기다리는 시간은 없고, 왔다 갔다 하면서 최대한 시간을 효율적으로 활용하는 거 같지만 엇갈리면 안 좋을 수도 있어.
잠시 sleep을 한 다음에 운영체제에게 다시 소유권을 넘긴 다음에 돌아오는 방법이고
 
 
3. 직원을 불러서 승무원 분께 부탁을 해서 화장실 비면 알려달라 부탁을 하는 거
자기 입장에서는 이득이지만, 다른 사람의 리소스를 사용하는 거. 직원에게 부담을 짊어지게 하는 거니까 갑질에 해당하는 부분이라고 볼 수 있다.
event를 사용하는 방법
 
이렇게 3가지 방법이 있고,
또 하나 고려해야 할 것은 화장실 앞에서 계속 대기를 타는 방법은 또 하나의 장점이 있는데
왔다 갔다 하는 비용이 없어진다는 장점이 생긴다.
 
첫번째 방법 제외한 두번째, 세번째 방법은 다 왔다 갔다 하는 이동 거리가 등장하게 된다.
이게 실질적으로 컴퓨터에서 컨텍스트 스위칭에 해당한다.
 
컨텍스트 스위칭이란?
식당 예제로 돌아와서 멀티스레드 복습을 해보면 식당 여러개 즉 프로그램 여러개 켜져있는 상태에서 스레드라는 직원을 배치해서 CPU 코어영혼이 되어 가지고 그 직원에 빙의해서 움직이는 그런 상황이라 했어. 사실상 이 직원이 식당에만 있는게 아니라 아래 부분에도 식당 관리자에 해당하는 직원이 있다.  영혼이 필요함.
 
백그라운드에서 실행되는 운영체제, 즉 윈도우즈도 결국에는 프로그램이다 보니까 CPU가 빙의해서 실행해줘야 한다.
 
프로그램이 실행이 될 때는 커널 모드 유저 모드가 있다. 여기서 보면 위에 있는 부분이 유저 모드이고, 아래 있는 부분이 커널 모드 이다.
 
커널 모드는 우리가 건드리는 건 아니고 마이크로 소프트나 운영체제 회사에서 만들어준 코드가 실행이 되는 그 운영체제를 관리하고 있는 관리자의 영역이다.
 
결국에는 어떤 직원이 실행이 되다가 다른 직원에게 빙의가 되어야 하는데 유저레벨에서 옮겨 타는게 아니라 항상 관리자 모드로 일단 돌아간 다음관리자 모드에서 다음에 실행이 될 애가 누구인지를 계산을 해서 그 애가 실행이 되게끔 유도를 해주게 될거야.
그래서 유저 레벨에서 커널 레벨(일반 직원에서 관리자 모드로) 들어가는 이 부분을 우리가 context switching 이라고 하는데 이 부분이 결국에는 예제에서 보면 화장실에 있다가 자리로 갔다가 다시 오는 이런 부분들에 해당한다고 보면 된다.
 
근데 유저 레벨에서 커널 레벨로 왔다 갔다 하는게 생각보다 부하를 많이 잡아 먹게 된다. 왜냐하면 그냥 곧이 곧대로 영혼만 빙의하고 끝인게 아니라 실질적으로 이 아이를 실행하기 위한 부가적인 정보가 많이 들어가 있어.

 

 
결국 요 아이가 무슨 생각을 하고 있었고, 어떤 위치에 있었고, 다음에 해야 되는 일은 무엇이었는지에 대한 모든 정보들 레지스터에 들어가게 되는데 이 레지스터는 다른 직원으로 갈아타게 되면 기존의 내용을 싸그리 다 RAM에다가 다시 저장을 하고 다른 애의 레지스터 값들을 다 불러 와가지고 복원을 시켜줘야 한다. 즉 완벽하게 어떤 직원으로 빙의하기 위해서는 그 직원이 생각하던 모든 생각들과 상황자체를 완전히 복원 시키고 그 기억을 저장하고 왔다 갔다 해야 한다는 거다.
 
그렇기 때문에 진짜로 이렇게 유저레벨에서 커널 레벨로 왔다 갔다 하는게 꽤 부담을 많이 잡아 먹는 부분이라고 할 수 있어. 그렇다고 해서 이게 절대로 해선 안된다는 건 아니고 결국 컨텍스트 스위칭은 일어 날 수 밖에 없어. 유저레벨과 커널레벨 모드로 왔다 갔다 하는 건 우리가 실행하는 모든 프로그램들이 그렇게 동작을 하는 거니까 최소한으로 줄여야 하는 건 맞지만 죄악이라는 건 아냐.
 
예를 들면 이 유저레벨에서 지금까지 사용하던 cout을 사용하여 콘솔 출력을 하는 것도 콘솔출력이라는 거 자체는 하드웨어와 관련된 문제이기 때문에 이 위의 유저레벨에서 곧이 곧대로 자기 멋대로 하드웨어를 조작하는게 불가능하다. 그리고 실질적으로 cout으로 뭔가 요청을 하면 내부적으로 위에 있는 유저 레벨에서 다시 커널 레벨로 돌아가서 커널레벨에서 이 관리자가 실질적으로 해당 부분을 우리 대신 실행을 해준다고 보면 된다. 우리는 위에서 아래로 뭔가 요청을 보내서 어떤 함수를 이용해서 요청을 보내는 것이지 윗단계에서 실행을 한다는 얘기는 아니야.
 
그래서 결국에는 이 컨텍스트 스위칭이라는 게 이렇게 정보들을 왔다 갔다 하면서 들고 있던 정보들을 저장했다가 다시 복원을 하고 이런 일련의 과정들이 다 들어간다는 것만 어렴풋이 이해를 해주면 된다. 컨텍스트 스위칭은 느리다. 스레드끼리 뭔가 변환이 일어날 때 일어나는 그런 일들이라고 기억을 해주면 된다.
 
이걸 어느정도 이해했으면 돌고 돌아서 처음에 이야기 하던 화장실에 사람이 있다면 무엇을 해야 되는가에서 존버메타라고 표현한 첫번째 방법에서는 유저레벨과 커널레벨을 왔다갔다 하는게 아니라 계속 유저 레벨에서 무한루프를 돌면서 계속 체크를 하게 될 거야.
 
얘는 컨텍스트 스위칭 비용이 없다는 장점이 있는 거고, 만약에 MMO같은 경우 만약 다른 스레드가 이 잠그고 있는 자물쇠를 금방 풀어줄거라는 어느정도 기대 혹은 확신이 있다면 이 spin lock을 활용하는 것도 빠르게 동작하는 것이고,
 
 
하지만 그게 아니라 좀 오래 걸릴 거 같다. 그렇다는 건 무작정 기다려도 쓸모가 없으니까 그럴 때는 다른 방법을 택하는게 괜찮을 수 있다. 이렇게 일단 자리로 돌아간다. context switching이 일어날 것이고, 커널레벨로 돌아가게 되는 부분들이 일어나게 될 것이고,
 
 
얘도 마찬가지로 커널레벨로 돌아 가면서 이제 이벤트라는 커널 오브젝트를 만들어가지고 실질적으로 상황이 딱 맞아 떨어질 때 이벤트를 호출해서 다시 돌아오는 그런 개념이 될거야. 
 
근데 이 3가지 방법이 있다고 해서 이 3개 중 하나를 골라야 된다는 건 아니고 적절하게 섞어서 써도 된다.
기다리다가 너무 오래 걸린다 하면 2번으로 하거나 하는 식으로 하게 될 거야.
 
지금 아직 정확히 얘네들이 어떤 역할을 하고 있고, 어떻게 구현을 해야 하는지 와 닿지 않겠지만 이건 다음 시간부터 코드를 구현해 가며 공부를 해 나갈거니 지금은 다 이해가 안 가도 상관 없어. 어렴풋이 이런 방법이 있구나 생각하면 된다.
 
그래서 다음 시간부터 코드로 이런 부분들을 구현을 해보도록 할 거야.
반응형

댓글