TIL: Asynchronous, MicroTask, Race Condition, Dead-Lock

Snoop So·2023년 2월 9일
0

Synchronous & Asynchronous

이름 때문에 매번 헷갈리는 동기와 비동기. 대체 어떤게 동시에 일어난다는 걸까?

'요청과 결과가 동시에 일어나는 것'이 바로 동기(Synchronous)이다. 그렇다면 비동기는? 요청과 결과가 동시에 일어나지 않는 것이다.

function runSetTimeOut() {
  console.log('call setTimeout');
  setTimeout(function () {
    console.log('after setTimeout');
  }, 2000);
}

console.log('start code');
runSetTimeOut();
console.log('call after setTimeout');

// 결과
// start code
// call setTimeout
// call after setTimeout
// after setTimeout

'call setTimeout' 이 실행하자마자 'after setTimeout'이 호출되지 않았다. 이것이 바로 비동기이다.

function runSetTimeOut() {
  console.log('call setTimeout');
  console.log(
    new Promise(function () {
      console.log('after setTimeout');
    })
  );
}

console.log('start code');
runSetTimeOut();
console.log('call after setTimeout');

// start code
// call setTimeout
// after setTimeout
// Promise { <pending> }
// call after setTimeout

그런데 이상한 점을 발견했다. pending 상태의 Promise는 즉각적으로 실행되어야 할 것 같은데 그렇지 않았다. 이는 마이크로태스크라는 개념과 연관된다.

MicroTask

비동기 작업을 처리하려면 적절한 관리가 필요한데, 이를 위해 ECMA에선 PromiseJobs 라는 내부 큐가 별도로 존재한다. V8에서는 이를 '마이크로태스크 큐(microtask queue)' 라는 용어로 부른다.

큐.. 큐라니? 내가 아는 건 스택이랑 힙 뿐이었는데 마이크로태스크 큐란 건 또 어디에 있는 어떤 친구인걸까.

'마이크로태스크 큐는 JavaScript 프로그램의 작업을 저장하는 곳입니다. 이 작업은 스택(stack)에서 관리되지 않습니다. 대신, 힙(heap)에서 관리됩니다. 힙은 컴퓨터 메모리의 동적 할당 영역입니다. 일반적으로, 프로그램이 실행되는 동안에 만들어지고 제거되는 객체와 변수를 저장하는 곳입니다.'

힙 메모리 안에 들어 있는, 비동기 작업을 위해 별도로 존재하는 메모리 영역인 것이다.

동작은 다음과 같이 이루어진다. 큐에 비동기 처리 작업들을 저장해두었다가, 다른 모든 코드가 실행하고 난 후에야 큐에 저장된 핸들러가 실행이 된다.

https://ko.javascript.info/microtask-queue

Race Condition

서로 다른 스레드에서 실행되는 코드가 공유하는 프로세스 메모리에 접근할 때 생기는 상태를 말한다. 그런데 이 공유하는 프로세스 메모리라는 게 어떤 말인지 궁금해서 ChatGPT 선생님께 여쭤봤다.

"공유하는 프로세스 메모리"란, 여러 스레드에서 동시에 접근할 수 있는 메모리 공간을 의미합니다. 예를 들어, 여러 스레드가 동시에 특정 변수의 값을 읽고/쓸 수 있는 경우 그 변수는 공유하는 프로세스 메모리에 해당합니다.

Race condition이 발생하면, 스레드들이 동시에 공유 메모리에 접근하여 값을 변경할 수 있기 때문에 예상치 못한 결과가 나올 수 있습니다. 이는 프로그램의 정상적인 동작을 방해하거나 프로그램이 예기치 않게 종료될 수 있는 문제를 발생시킵니다."

읽어보면 변수의 값이 변경이 될 경우 주로 문제가 발생하는 것 같다. "Race Condition 상태에서 변수가 변경될 때 의도한 결과를 얻을 수 없거나 프로그램이 예기치 않게 종료되는 문제가 발생하므로 주의해야 한다", 정도를 이해하면 될 것 같다.

이를 해결하기 위해 임계 영역을 보호하기 위해 세마포어나 뮤택스를 사용해야 한다. 너무 딥하게 들어가지 않기 위해 이는 다음에 알아보도록 하자 ^ㅡ^

https://iredays.tistory.com/125
https://dduddublog.tistory.com/25

Dead-Lock

임계 영역을 보호해준다고 해서 문제가 해결되지 않는다. 대표적인 예제가 바로 '승려 문제'이다. 동그란 의자에 앉은 5명의 승려 사이에 젓가락이 4개만 존재하면 어떤 젓가락은 두 명의 승려에게 공유되므로 교착 상태가 발생한다. 이를 프로세스나 스레드에 대입해 생각해보면 데드락의 개념을 이해할 수 있다.

DeadLock은 두 개 이상의 프로세스 또는 스레드가 서로의 자원을 사용하려 기다리는 상황을 말한다. 그런데 여기까지 공부하고 나니 헷갈린다.. DeadLock이랑 Race Condition 은 어떻게 다른거지?

다시 ChatGPT 선생님의 이야기를 들어보자.

'Race condition은 두 개 이상의 스레드가 동시에 같은 데이터에 접근하여 데이터의 상태를 변경하는 경우에 발생한다. 예를 들어, 두 개의 스레드가 동시에 같은 변수에 값을 쓰려고 할 때, 이는 race condition으로 알려져 있다.

Deadlock은 두 개 이상의 스레드가 서로 다른 리소스를 점유하고 있을 때, 어느 한 쪽의 리소스를 풀어주지 않아서 다른 스레드가 점유한 리소스를 얻지 못하는 상태를 말한다. 이러한 상태로 인해 프로세스가 정지되게 되는데, 이를 deadlock이라 한다.

두 문제 모두 동시성 프로그래밍에서 상당히 중요한 문제이지만, 접근하는 방식이나 해결하는 방법이 다르다.'

그러니까 Race condition은 동시에 리소스에 접근할 때, Dead Lock은 이미 점유하고 있는 리소스에 접근할 때 발생한는 문제를 가르키는 것이라고 이해하면 될 것 같다.

0개의 댓글