본문 바로가기
CS/BOOK

[한 권으로 읽는 컴퓨터 구조와 프로그래밍] 12. 컴퓨터는 어떻게 멀티태스킹을 할까?

by 벨롭 2024. 4. 30.

 

병렬성

 

 

 

초기의 컴퓨터는 하나의 CPU만을 가지고 있었다. 그렇기 때문에 두 가지 일을 동시에 처리하는 대신, 동시에 처리하는 '척'을 했다. 예를 들면 주황-초록-보라 순서대로 아주 조금씩 작업을 하는 것이다. 사람이 캐치하기 어려울 만큼 작은 단위로 일을 쪼개서 하면, 사람은 마치 동시에 일을 하는 것처럼 느낄 수 있는 것이다.

 

하지만 멀티 프로세서가 일반화되면서, '진짜로' 동시에 일을 처리해야하는 상황에 처하게 되었다. 동시에 일을 처리한다면, 같은 자원에 동시에 접근하는 경우도 발생할 수 밖에 없다.

 

 

 

 

 

 

 

0x0001이라는 메모리에 100이라는 값이 저장되어 있고, 10을 빼서 저장하는 프로세스1과 20을 빼서 저장하는 프로세스2가 이 메모리에 동시에 접근하는 경우를 생각해보자. 두 프로세스가 실행된 후 기대하는 결과는 다음과 같을 것이다. 

프로세스1 프로세스2 0x0001
    100
0x0001 읽음   100
읽은 값에서 10을 뺌 (100 - 10)   100
0x0001에 저장   90
  0x0001 읽음 90
  읽은 값에서 20을 뺌 (90 - 20) 90
  0x0001에 저장 70

 

 

하지만 이런 결과가 생길 수도 있다.

프로세스1 프로세스2 0x0001
    100
0x0001 읽음   100
읽은 값에서 10을 뺌 (100 - 10)   100
  0x0001 읽음 100
  읽은 값에서 20을 뺌 (100 - 20) 100
0x0001에 저장   90
  0x0001에 저장 80

 

 

 

 

 

 

 

0x0001와 같은 자원을 공유자원이라고 하는데, 이와 같은 상황에 대한 적절한 처리가 없으면 기대한 결과값이 나오지 않을 수 있다. 이 문제를 해결하기위해, 프로세스의 흐름을 하나의 트랜잭션(실행 흐름의 단위)로 묶어서 한꺼번에 처리하도록 만들 수 있다. 그리고 그동안은 다른 프로세스가 자원에 접근하지 못하도록 락을 걸어둔다.  

 

하나의 트랜잭션
0x0001 읽음
읽은 값에서 n을 뺌 (100 - n)
0x0001에 저장

 

 

 

 

 

이같은 해결 방법에도 문제점은 존재한다. 락이 풀릴 때까지 다른 프로세스가 대기해야하기때문에 멀티태스킹의 목적과는 멀어질 수 있다. 공유자원에 락이 풀릴때까지 반복적으로 시도하며 락이 풀렸는지 체크하거나, 대기열에서 기다리다가 순서가 되면 들어가는 것 정도의 방법적인 차이는 있지만 대기가 발생한다는 것은 어쩔 수 없다.

 

 

 

또는 교착상태에 빠질 수 있다. 프로세스1은 자원 2를 가지고 있는데, 자원 1을 기다리고 있다. 프로세스2는 자원 1을 가지고 있는데, 자원 2를 기다리고 있다. 서로가 가진 자원을 내놓기를 기다리면서 무한정 대기해야하는 상황에 빠지는 것이다.  

 

* 교착상태는 아래의 네 가지 조건을 동시에 만족하는 경우에 발생한다. 즉, 교착 상태를 해결하기 위해서는 네 가지 중 한 가지 이상의 요인을 해결해야 한다.

1) 상호배제 : 공유자원을 어느 한 프로세스가 독점적으로 사용함
2) 점유 대기 : 자원을 점유한 상태에서 다른 자원을 요청함
3) 비선점 : 자원을 강제로 빼앗을 수 없음
4) 순환 대기 : 서로 순환적으로 다른 프로세스가 갖고 있는 자원을 요구함.

 

 

 

 

 

 

 

 

 

비동기성

 

 

 

 

 

 

멀티태스킹의 병렬성 문제를 해결하기 위해, 비동기적인 통신을 이용하는 것은 좋은 아이디어가 될 수 있다. 요청한 응답이 도착할 때까지 대기하는 것이 아니라, 도착할 동안에도 멈추지 않고 동작하는 것이 비동기성이고, 이와 같은 웹브라우저의 통신 스타일을 AJAX라고 한다.

 

 

 

 

 

책에서는 jQuery로 예시를 들었지만, 본 글에서는 자바스크립트 예시코드를 작성하였습니다.

 

 

 

자바 스크립트에서는 AJAX를 구현하기 위해 XMLHttpRequest 객체를 활용한다.

 

const DONE = 4
const OK = 200

// XMLHttpRequest 객체 생성
var xhr = new XMLHttpRequest();

// 콜백 함수 설정 => xhr의 readyState 상태가 변할때마다 함수가 호출됨
xhr.onreadystatechange = function () {
    if (xhr.readyState === DONE && xhr.status == 200) {
       console.log("모든 데이터가 전송 완료 되었습니다.")
       console.log("서버데이터 요청 결과는 성공입니다..")
    }
}
    
// 비동기화 요청 - 요청 방식, 호출 페이지 등록
xhr.open("GET", "http://www.example.com")
    
// 요청 전송
xhr.send()

 

 

 

XMLHttpRequest 객체를 사용하게 되면 콜백이 중첩된 스파게티 코드가 만들어지기 쉽기 때문에 조금 더 현대적인 방법으로, Promise-Style을 활용하기도 한다.

 

간단한 Promise의 예시 코드는 아래와 같다.

// resolve : 이행되었을때의 콜백함수 , reject : 거부된 경우의 콜백 함수
const myPromise = new Promise((resolve, reject) => {
	resolve('성공했습니다.') // then 부분 실행
}

myPromise
    .then((data) => { console.log(data) })

 

 

Promise를 활용하면, fetch API를 통해 비동기 요청을 보낼 수 있다.

// Http 응답을 나타내는 Response 객체를 래핑한 Promise 객체를 반환
fetch("http://www.example/com")
    .then((response) => console.log(response.text()))	// 성공시 실행
    .catch(console.log("실패하였습니다.")		// 실패시 실행

 

 

 

 

 

-- 확인 문제--

 

1. 멀티태스킹을 위해 비동기적인 통신방법인 AJAX를 활용할 수 있다. AJAX를 구현하기위한 코드의 빈칸에 들어갈 말은?

const DONE = 4
const OK = 200

// 비동기 통신을 위한 객체 생성
var xhr = new _______(A)_________();

// 콜백 함수 설정 => xhr의 readyState 상태가 변할때마다 함수가 호출됨
xhr._________(B)_________ = function () {
    if (xhr.readyState === DONE && xhr.status == 200) {
       console.log("모든 데이터가 전송 완료 되었습니다.")
       console.log("서버데이터 요청 결과는 성공입니다..")
    }
}
    
// 비동기화 요청 - 요청 방식, 호출 페이지 등록
xhr.___(C)__("GET", "http://www.example.com")
    
// 요청 전송
xhr._____(D)_____()

 

 

2. 병렬성의 문제에서, 공유자원이 교착 상태가 발생하는 원인은 무엇인가 ? 

1) _____ : 공유자원을 어느 한 프로세스가 독점적으로 사용함
2) _____ : 자원을 점유한 상태에서 다른 자원을 요청함
3) _____ : 자원을 강제로 빼앗을 수 없음
4) _____ : 서로 순환적으로 다른 프로세스가 갖고 있는 자원을 요구함.

 

 

 

-- 정답 --

더보기

1. (A) XMLHttpRequest

    (B) onreadystatechange

    (C) open

    (D) send

 

2. 1) 상호배제

    2) 점유대기

    3) 비선점

    4) 순환 대기