CS와 Node.js 8. 비동기! 작업 스케쥴링, 멀티 스레딩&프로세싱, 임계 구역, JS 비동기, 이벤트루프, 타이머

박재하·2023년 11월 3일
0

CS와 Node.js

목록 보기
8/12

작업 스케줄링

폰 노이만 구조와 하버드 구조

폰 노이만 구조와 하버드 구조

멀티 스레드 스케줄링

멀티 스레드 스케쥴링

멀티 스레드

멀티 스레딩이란 실행 중인 프로그램인 하나의 프로세스를 여러 개의 작업 단위로 구성하고, 여러 개의 스레드를 생성해 각 스레드에게 하나씩 작업을 할당하는 작업 처리 방식입니다.

멀티 스레딩은 시스템 자원 소모 감소, 시스템 처리량 증가, 간단한 통신 방법 등 다양한 장점으로 인해 효율성이 요구되는 작업에 자주 사용됩니다.

  • 자원의 효율성 증대
    • 멀티 프로세스는 프로세스를 하나 더 생성하여 자원을 할당하는 방식이기 때문에, 프로세스 간의 Context Switching 시 CPU 레지스터 교체 뿐 아니라 RAM, CPU 사이 캐시 메모리 데이터가 모두 초기화되므로 자원 소모가 더욱 커집니다.
    • 반면 멀티 스레드는 프로세스 내에서 메모리를 공유하기 때문에 스레드 간 데이터 교환이 간단해지고 자원 소모가 적습니다.
  • 처리 비용 감소 및 응답 시간 단축
    • 상기된 이유로 프로세스 간 통신(IPC)에는 파이프, 파일, 소켓 등을 이용합니다만, 스레드는 공유 메모리를 사용하므로 통신 시간도 단축되고 통신 관련 자원 소모가 적습니다.

하지만 동시에, 동작 방식이 복잡해 디버깅이 까다로우며 자원 공유의 (동기화) 문제 등이 발생합니다.

동기화 문제는 SemaphoreMutex로 대표되는 임계구역(Critical Section)과 관련된 문제인데, 아래에서 다뤄보겠습니다.

스케줄링 방식 비교

미션에서 제시된 프로세스 스케줄링 방식을 다시 한번 정리해 보겠습니다.

  • 기한부 스케줄링 (deadline scheduling)
    • 모든 프로세스의 작업 동작시간을 주어진 마감 시간까지 완성하도록 계획한 스케줄링
  • 우선순위 스케줄링 (priority scheduling)
    • 우선순위가 높은 프로세스를 먼저 실행
    • 동적으로 우선순위가 바뀔수도 있고, 고정적인 우선순위로 운영되기도 함
  • FIFO 스케줄링 (FIFO scheduling)
    • 비선점으로 프로세스가 생성된 순서대로 대기 큐에 넣어놓고 순서대로 스케줄링
  • SJF 스케줄링 (Shortest Job First scheduling)
    • 실행 시간이 가장 짧은 프로세스부터 먼저 실행하는 방식
  • 라운드로빈 스케줄링 (Round Robin scheduling)
    • 선점 스케줄링 방식으로 FIFO처럼 순차적으로 실행하지만, 제어 시간을 일정하게 나눠서 스케줄링하는 방식
    • 시간이 끝났는데 작업이 끝나지 않았으면 가장 낮은 우선순위로 넘어감

스레드 스케줄링

여러 자료를 보면 확인할 수 있듯이, 스레드 스케줄링도 프로세스 스케줄링과 완전히 똑같은 전략들을 이용할 수 있습니다. 위의 내용에서 프로세스스레드로만 바꿔주면 모두 매치됩니다.

Scheduling Priorities - Win32 apps

Mach Scheduling and Thread Interfaces

FIFO, Round-Robin 등 다양한 전략을 제공하는 듯 합니다만, 일반적으로 Round-Robin을 사용한다는 것 같습니다. 정확히는 찾기가 힘드네요.

node.js의 스레딩 방식

Node.js의 동작 원리 |

스케줄링 메커니즘을 따로 구현하진 않았지만, Node.js의 동작 원리를 통해 확인할 수 있듯이, 싱글 스레드 언어인 JS를 보완하기 위해 libuv비동기, 동시성, 논블로킹을 구현해준다고 합니다.

%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2023-07-20_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_8 15 06

결론적으로 node에서 제공하는 worker_thread 모듈을 이용한 저는 libuv에서 제공하는 **Round-Robin **방식으로 구현된 것으로 해석할 수 있겠습니다.

하지만 결과적으로 같은 페이즈 안에서는 FIFO 순서로 콜백 함수를 처리한다고 하네요. timer 페이즈가 그 중 하나이므로 FIFO가 아닐까 예상됩니다.

명시적으로 나와있지 않아 시간을 내서 Node.js의 내부 구조를 더 상세히 파악해봐야 겠습니다.

타이머

setTimeout

🔄 자바스크립트 이벤트 루프 동작 구조 & 원리 끝판왕

https://youtu.be/v67LloZ1ieI

JS는 setTimeout과 같이 비동기로 동작하는 함수를 이벤트 루프라는 데서 관리합니다.

이벤트 루프는 다음의 과정으로 동작합니다.

setTimeout(() => {
  console.log("> done");
}, 1000);

foo();
%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2023-07-20_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_8 32 33
  1. setTimeout()이 호출되면 콜 스택에 쌓입니다.
  2. setTimeout()의 매개변수에 할당된 콜백 함수 ()⇒{console.log(”> done”);}Timer Web API에 전달됩니다.
  3. Timer Web API는 백그라운드로 1000ms(1초)를 셉니다.
  4. 시간이 지나면, 이벤트 루프Time Web API가 가지고 있던 콜백 함수 ()⇒{console.log(”> done”);} 를 다시 Task Queue로 옮깁니다.
  5. 그 사이 Call Stack에서는 열심히 setTimeout() 아래의 함수(foo())들이 실행됩니다. Call Stack이 완전히 빌 때 까지!
  6. 그 후 이벤트 루프는 Call Stack이 비어있는 경우를 탐지하여, Task Queue에 있는 콜백 함수 ()⇒{console.log(”> done”);}를 다시 Call Stack으로 옮깁니다.
  7. 마침내 Call Stack에서는 콜백 함수 ()⇒{console.log(”> done”);}를 실행하게 되고, 콘솔창에 > done이 출력됩니다.

정말 정말 길고 복잡해서 머리가 아프긴 하지만, 미션 해결 과정에서 Promise를 만들고 무한 루프로 값 변화를 기다렸을 때 도대체 왜 값이 변하지 않는지를 드디어 이해했습니다!!!

이후 코드가 다 실행되어야 콜백이 실행되는거였구나…

엄청난 깨달음입니다. async/await.. 너무 머리아프지만 꼭 배워야하는거였군요.

그 외의 타이머 구현 방식들

setInterval()

javaScript(타이머함수-setTimeout, setInterval, clearTimeout, clearInterval)

대표적으로 setInterval()이 있습니다. setTimeout()과 달리 시간 간격마다 함수를 실행합니다.

  • setTimeout(함수, 시간): 일정 시간 후 함수 실행
  • setInterval(함수, 시간): 시간 간격마다 함수 실행
  • clearTimeout(): 설정된 Timeout 함수를 종료
  • clearInterval(): 설정된 Interval 함수를 종료

requestAnimationFrame()

[JS-기초편] 7.타이머(Timers)

Window: requestAnimationFrame() method - Web API | MDN

또 JS로 게임이나 애니메이션을 만들 때 대표적으로 사용되는 requestAnimationFrame()이 있습니다. 이 함수는 사용 방법이 좀 독특합니다.

function animationLoop() {
  // animation-related code

  requestAnimationFrame(animationLoop);
}

// start off our animation loop!
animationLoop();

위와 같이 animationLoop()를 만들면, 무한 재귀 호출인 것 같지만 브라우저에서 이 부분이 특별히 처리되어 로컬 PC의 초당 프레임 수에 맞게 루프를 반복합니다.

이를 통해 크롬 공룡게임 같은 다양한 게임을 만들 수 있습니다! ㅎ 관련해서 제가 좋아하는 코딩애플님의 영상을 또 첨부해놓겠습니다.

requestAnimationFrame의 사용 방법이 쉽게 소개되어 있습니다.

코딩애플 - requestAnimationFrame

new Date()

현재 시간을 가져올 수 있는 .now() 함수를 이용해서 타이머를 만들수있다!

타임스탬프를 찍고, 몇초 지나면 그 값을 비교해서

멀티 스레드 임계구역

멀티 스레드의 임계 구역과 뮤텍스, 세마포어

임계구역

경쟁상태, 임계영역의 개념과 동기화를 위한 여러 상호배제 기법 (mutex, semaphore, monitor)

임계구역 또는 임계영역(Critical Section)은 한 개의 연산을 둘 이상의 동시에 호출된 쓰레드하나의 공유 자원에 접근해서 문제가 발생하는 코드 영역 또는 함수를 말합니다.

이런 코드가 실행되면 둘 이상의 입력이나 조작실행 순서에 따라 결과가 달라지게 되는데, 이를 경쟁 상태(Race Condition)라고 합니다.

교착 상태

[OS] 교착상태란 무엇인가?

경쟁 상태 중 특히 둘 이상의 스레드가 서로 점유하는 자원을 요구하며 무한정 기다리는 현상을 교착 상태(Dead Lock)라고 합니다.

뮤텍스와 세마포어

교착 상태가 일어나면 프로그램이 멈춰버리므로 절대 일어나지 않도록 예방, 회피, 탐지, 복구 등으로 해결해줘야 합니다.

다음 두 개념(세마포어뮤텍스)는 교착 상태를 방지하는 대표적인 해결 방법입니다.

Semaphore

%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2023-07-20_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_9 05 53

세마포어는 공유 자원에 접근할 수 있는 최대 허용치만큼 동시 사용자(쓰레드, 프로세스)의 접근을 허용하게 하는 전략입니다.

Mutex

%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2023-07-20_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_9 07 06

뮤텍스Key에 해당하는 어떠한 오브젝트를 두고, 이 오브젝트를 소유한 사용자(쓰레드, 프로세스)만이 공유자원에 접근할 수 있도록 통제하는 전략입니다.

Mutual Exclusion의 줄임말로, 상호 배제라는 뜻입니다.

두 방식의 비교

  • 뮤텍스는 Key를 가진 사용자(쓰레드/프로세스)만 자원을 소유하기 때문에 그 사용자가 Lock을 해제하기 위한 책임을 가집니다.
    • 해제하지 않는 경우 영원히 자원을 점유합니다. 또다른 문제!
  • 세마포어는 세마포어를 소유하지 않는 사용자(쓰레드/프로세스)가 이 세마포어를 해제할 수 있습니다.
    • 따라서 반대로 다른 사용자가 작업 중 자원 점유를 강제로 해제해버리는 문제가 생깁니다.

장단점도 있고, 둘 다 완벽한 개념이 아니기 때문에 상황에 맞게 두 개념 모두 적절히 변환해서 사용하는 것을 추천한다고 합니다.

[Philosophers] 예시/예제로 보는 뮤텍스와 세마포어의 차이

주로 뮤텍스는 같은 종류의 공유 자원이 하나일 때, 세마포어여러 개일 때 사용하면 되겠습니다.

비유하자면, 뮤텍스화장실이 하나열쇠를 입구에 걸어둔 식당, 세마포어화장실이 여러 개라 입구전광판에 빈 칸의 개수가 적혀있는 식당이라네요. 이해 쏙쏙?

profile
해커 출신 개발자

0개의 댓글