반응형
본 글은 책 컴퓨터 밑바닥의 비밀 2.6 장 내용을 요약한 내용입니다.
고된 프로그래머 예시를 통한 동기/비동기 비교
- 상사가 프로그래머에게 일을 시킬 때, 옆에서 계속 기다리고 있으면 동기, 일을 시키고 다른 일을 하면 비동기라고 예를 들어 설명
또 다른 예로 전화통화와 이메일을 비교해보면,
- 전화통화 : 상대방이 말할 때 들으며 기다려야 함(동기)
- 이메일: 작성하는 동안 다른 사람들은 다른 일 처리 가능(비동기)
동기 호출
- 일반적인 함수 호출
funcA()
{
// funcB 함수가 완료될 때까지 기다림
funcB();
// funcB 함수는 프로세스를 반환하고 계속 진행
}
입출력 작업의 경우,
...
read(file, buf); // 여기에서 실행이 중지됨
...
// 파일 읽기가 완료될 때가지 기다렸다가 계속 실행함
- 최하단 계층은 실제로 system call로 운영체제에 요청을 보냄
- 파일 읽기 작업을 위해 read 호출 스레드를 일시 중지하고, 커널이 디스크 내용을 읽어 오면 일시 중지 되었던 스레드가 다시 깨어남 (Blocking input/output 이라고 함)
비동기 호출
- 디스크의 파일 읽고 쓰기, 네트워크 IO, 데이터 베이스 작업처럼 시간이 많이 걸리는 입출력 작업을 백그라운드 형태로 실행
read(file, buf); // 여기에서 실행이 중지되지 않고 즉시 반환
// 이후 내용의 실행을 블로킹하지 않고 바로
- read 함수가 비동기 호출되면 파일 읽기 작업이 완료되지 않은 상태에서도 read 함수는 즉시 반환될 수 있음
- 호출자가 블로킹 되지 않고 read 함수가 즉시 반환되기 때문에 다음 작업을 실행할 수 있음
그럼 비동기 호출에서 파일 읽기 작업이 언제 완료되었는지 어떻게 알 수 있을까? 이 경우, 처리에 대한 2가지 상황이 있을 수 있음
- 호출자가 실행 결과를 전혀 신경쓰지 않을 때
- 호출자가 실행 결과를 반드시 알아야 할 때
1. 호출자가 실행 결과를 전혀 신경쓰지 않을 때
void handler(void* buf)
{
... // 파일 내용 처리 중
}
read(buf, handler)
"계속해서 파일을 읽고, 작업이 완료되면 전달된 함수(handler)를 이용해 파일을 처리해주세요."
⇒ 파일 내용은 호출자 스레드가 아닌 콜백 함수가 실행되는 다른 스레드(호출되는 스레드) 또는 프로세스 등에서 처리
2. 호출자가 실행 결과를 반드시 알아야 할 때
- notification 작동 방식을 사용하는 것
- 작업 실행이 완료되면 호출자에게 작업 완료를 알리는 신호나 메시지를 보내는 것
- 결과 처리는 이전과 마찬가지로 호출 스레드에서 함
웹 서버에서 동기와 비동기 작업
아래의 작업을 한다고 가정
- A, B, C 세 단계를 거친 후 데이터 베이스를 요청
- 데이터 베이스 요청 처리가 완료 되면 D, E, F 세 단계를 거침
- A, B, C, D, E, F 단계에는 입출력 작업이 포함되어 있지 않음
// 사용자 요청을 하는 단계
A;
B;
C;
데이터 베이스 요청;
D;
E;
F;
먼저 가장 일반적인 동기 처리 방식
// 메인 스레드
main_thread()
{
while(1)
{
요청 수신;
A;
B;
C;
데이터베이스 요청을 전송하고 결과가 반환될 때까지 대기;
D;
E;
F;
결과 반환;
}
}
// 데이터베이스 스레드
database_thread()
{
while(1)
{
요청 수신;
데이터베이스 처리;
결과 반환;
}
}
- 데이터 베이스 요청 후 주 스레드가 블로킹 되어 일시 중지됨
- 데이터 베이스 처리가 완료된 시점에서 D, E, F가 계속 실행됨
- 주 스레드에서 빈공간은 유휴 시간(idle time)으로 기다리는 과정
- 유휴 시간을 줄이기 위해서 비동기 작업을 활용할 수 있다
1. 주 스레드가 데이터 베이스 처리 결과를 전혀 신경 쓰지 않을 때
- 주 스레드는 데이터 베이스 처리 완료 여부 상관하지 않음
- 데이터베이스 스레드가 D, E, F 세 단계를 자체적으로 직접 처리
- 데이터 베이스 처리 후 DB 스레드가 D, E, F 세 단계를 알 수 있는 방법은?
- 콜백 함수
void handle_DEF_after_DB_query()
{
D;
E;
F;
}
주 쓰레드가 데이터베이스 처리 요청을 보낼 때 위 함수를 매개변수로 전달
DB_query(request, handle_DEF_after_DB_query);
- 데이터 베이스 쓰레드는 데이터 베이스 요청을 처리한 후 handle_DEF_after_DB_query 함수 호출하기만 하면 됨
이 함수를 데이터 베이스 쓰레드에 정의하고 직접 호출하는 대신 콜백 함수를 통해 전달받아 실행하는 이유은?
⇒ 소프트웨어 조직 구조 관점에서 볼 때 데이터베이스 쓰레드에서 해야 할 작업이 아니기 때문
2. 주 쓰레드가 DB 작업 결과에 관심을 가질 때
- 알림 작동 방식을 이용하여 작업 결과를 주 스레드로 전송
- 주 스레드는 사용자 요청의 후반부를 계속 처리
- 주 스레드에는 유휴시간이 없음
- 비 동기 호출만큼 극단적으로 효율적이진 않지만 동기 호출에 비하면 여전히 효율적
반응형
'OS' 카테고리의 다른 글
Event loop와 coroutine (0) | 2024.08.11 |
---|---|
블로킹/논블로킹 (0) | 2024.08.10 |
파일 시스템 (0) | 2024.07.28 |
가상 메모리(Virtual memory) (0) | 2024.07.28 |
교착상태(Deadlock) (0) | 2024.07.19 |