동기와 비동기 블럭과 논블럭에 대해 알아보자.
Blocking VS Non-blocking
블럭/논블럭은 간단히 말해서 호출된 함수가 호출한 함수에게 제어권을 건네주는 유무의 차이라고 볼 수 있다.
함수 A, B가 있고, A 안에서 B를 호출했다고 가정해보자. 이때 호출한 함수는 A고, 호출된 함수는 B가 된다. 현재 B가 호출되면서 B는 자신의 일을 진행해야 한다. (제어권이 B에게 주어진 상황)
- Blocking : 함수 B는 내 할 일을 다 마칠 때까지 제어권을 가지고 있는다. A는 B가 다 마칠 때까지 기다려야 한다.
- Non-blocking : 함수 B는 할 일을 마치지 않았어도 A에게 제어권을 바로 넘겨준다. A는 B를 기다리면서도 다른 일을 진행할 수 있다.
즉, 호출된 함수에서 일을 시작할 때 바로 제어권을 리턴해주느냐, 할 일을 마치고 리턴해주느냐에 따라 블럭과 논블럭으로 나누어진다고 볼 수 있다.
* 제어권: 제어권은 자신(함수)의 코드를 실행할 권리 같은 것이다. 제어권을 가진 함수는 자신의 코드를 끝까지 실행한 후, 자신을 호출한 함수에게 돌려준다.
위의 Blocking Non-blocking개념을 활용한 I/O 방식을 보자.
Blocking I/O 방식
I/O Blocking 형태의 작업은
- Process(Thread)가 Kernel에게 I/O를 요청하는 함수를 호출한다.
- Kernel이 작업을 완료하면 작업 결과를 반환 받는다.
- 특징
- I/O 작업이 진행되는 동안 user Process(Thread) 는 자신의 작업을 중단한 채 대기한다.
- 자원 낭비가 심하다. (I/O 작업이 CPU 자원을 거의 쓰지 않으므로)
여러 Client 가 접속하는 서버를 Blocking 방식으로 구현하는 경우 => I/O 작업을 진행하는 작업을 중지 => 다른 Client가 진행중인 작업을 중지하면 안되므로, client 별로 별도의 Thread를 생성해야 함 => 접속자 수(스레드 개수)가 매우 많아짐
이로 인해, 많아진 Threads 로 컨텍스트 스위칭 횟수가 증가함 => 비효율적인 동작 방식
Non-Blocking I/O 방식
I/O 작업이 진행되는 동안 User Process의 작업을 중단하지 않는다.
- 진행 순서
- User Process가 recvfrom 함수 호출 (커널에게 해당 Socket으로부터 data를 받고 싶다고 요청함)
- Kernel은 이 요청에 대해서, 곧바로 recvBuffer를 채워서 보내지 못하므로, 아직 작업 진행중이라는 의미로 "EWOULDBLOCK"을 return한다.
- Blocking 방식과 달리, User Process는 다른 작업을 진행할 수 있다.
- recvBuffer에 user가 받을 수 있는 데이터가 있는 경우, Buffer로부터 데이터를 복사하여 받아온다.
- 이때, recvBuffer는 Kernel이 가지고 있는 메모리에 적재되어 있으므로, Memory간 복사로 인해, I/O보다 훨씬 빠른 속도로 data를 받아올 수 있다.
- recvfrom 함수는 빠른 속도로 data를 복사한 후, 복사한 data의 길이와 함께 반환한다.
Synchronous VS Asynchronous
- 동기(Syncronous)
- 동시에 일어난다는 뜻이다. 요청과 그 결과가 동시에 일어난다는 약속이다.
- "동시에"라는 말은 시간을 맞춘다는 뜻이다. 쉽게 말해 함수가 두 개 이상 존재할 때, 이 함수들이 작업을 동시에 시작하거나, 끝나는 타이밍을 맞추거나, 하나가 끝나고 다른 하나를 차례로 실행하는 것을 시간을 맞춘다고 한다.
- 즉, 바로 요청을 하면 시간이 얼마가 걸리던지 요청한 자리에서 결과가 주어져야 한다.(요청 => 결과 차례로 실행)
- 특징
- 한 자원에 동시에 접근하는 것을 제한한다.
- 순차적으로 진행한다.
- 다음에 실행될 명령은 현재 실행 중인 명령 종료 시까지 대기해야 한다. (대기시간 버퍼링 발생)
- 서버와 클라이언트가 주고 받는 것이 동시에 이루어지는 형태이다.
- A노드와 B노드 사이의 작업 처리 단위(transaction)를 동시에 맞추겠다.
- 시간적인 동기화가 필요한 곳에 많이 사용한다. ex) 현금인출기
- Java에서 synchronized 키워드 사용
- 자바에서 멀티 스레드 접근 제한 키워드
- 메소드 단위, 블록 단위 적용 가능
- 단, 메소드 단위로 지정할 경우 메소드 전체에 lock이 걸리기 때문에 가능하면 블록 활용 (임계 영역은 작을 수록 좋음)
- 동기 방식은 설계가 매우 간단하고 직관적이지만 결과가 주어질 때까지 아무것도 못하고 대기해야 한다는 단점이 있다.
- 동시에 일어난다는 뜻이다. 요청과 그 결과가 동시에 일어난다는 약속이다.
* 트랜잭션: 논리적인 업무 처리의 최소 단위
- 비동기(Asyncronous)
- 동시에 일어나지 않는다를 의미한다. 요청과 결과가 동시에 일어나지 않을 것이라는 약속이다.
- 비동기의 경우, 이벤트 큐에 넣거나 백그라운드 스레드에게 해당 task를 위임하고 바로 다음 코드를 실행하기 때문에 기대되는 값이 동시에 반환되지 않는다.
- 요청한 그 자리에서 결과가 주어지지 않는다.
- 노드 사이의 작업 처리 단위를 동시에 맞추지 않아도 된다.
- 특징
- 현재 실행 중인 명령이 종료되지 않아도 다음 명령 실행 가능하다.
- Callback 함수를 통해 반환 결과를 확인한다.
- 비동기 방식은 동기보다 복잡하지만, 결과가 주어지는데 시간이 걸리더라도 그 시간 동안 다른 작업을 할 수 있으므로 자원을 효율적으로 사용할 수 있다는 장점이 있다.
- 동시에 일어나지 않는다를 의미한다. 요청과 결과가 동시에 일어나지 않을 것이라는 약속이다.
Synchronous/Asynchronous는 호출되는 함수의 작업 완료 여부를 누가 신경쓰냐가 관심사다.
아까처럼 함수 A와 B라고 똑같이 생각했을 때, B(호출된 함수)의 수행 결과나 종료 상태를 A(호출한 함수)가 신경쓰고 있는 유무의 차이라고 생각하면 된다.
- Synchronous : 함수 A는 함수 B가 일을 하는 중에 기다리거나 현재 상태가 어떤지 계속 체크한다.
- Asynchronous : 함수 B의 수행 상태를 B 혼자 직접 신경쓰면서 처리한다. (Callback)
동기는 요청과 동시에 결과값이 주어진다. 그래서 호출한 함수가 호출된 함수의 작업 완료 후 리턴값 반환을 기다리거나(요청과 동시에 결과값을 받기 위해), 또는 호출되는 함수로부터 바로 결과값(결과값이 반환되지 않았다는 결과값)을 리턴 받더라도(요청과 동시에 결과값을 받음) 작업 완료 여부를 계속 확인하며 신경쓴다.(실제 결과값이 아직 나오지 않았다는 걸 알았으니 계속 언제 나오는지 체크)
비동기는 호출시 호출된 함수에게 Callback을 전달하여 호출된 함수의 작업이 완료되면 호출된 함수가 전달받은 callback을 실행하고, 작업의 완료 여부를 호출한 함수에게 답하게 된다. (Callback이 오기 전까지 호출한 함수는 작업 완료 여부를 신경쓰지 않고 다른 일을 할 수 있음, 즉 요청과 동시에 결과값이 반환되지 않더라도 신경쓰지 않음)
즉, 호출된 함수(B)를 호출한 함수(A)가 신경쓰는지, 호출한 함수 (A)가 신경쓰지 않고 호출된 함수(B) 스스로 신경쓰는지를 동기/비동기라고 생각하면 된다.
* 결과값: 결과값은 우리가 보통 말하는 함수의 리턴값이다. 여기서 결과값은 3이 될 것이라고 쉽게 예상할 수 있다. 그런데 꼭 결과값은 3이 아니어도 된다. 만약 A가 아직 계산을 끝마치지 못했는데, 갑자기 결과값을 요구한다면 '아직 계산못함'을 임시 결과값으로 내보낼 수도 있다.
Java synchronized 키워드 적용 예시
- synchronized 키워드 적용하지 않은 경우
class User {
private int userNo = 0;
// 임계 영역이 되어야하는 메소드
public void add(String name) {
System.out.println(name + " : " + userNo++);
}
}
class UserThread extends Thread {
User user;
UserThread(User user, String name) {
super(name);
this.user = user;
}
public void run() {
try {
for (int i = 0; i < 3; i++) {
user.add(getName());
sleep(500);
}
} catch(InterruptedException e) {
System.err.println(e.getMessage());
}
}
}
public class SyncThread {
public static void name(String[] args) {
User user = new User();
// 3개의 스레드 객체 생성
UserThread p1 = new UserThread(user, "A");
UserThread p2 = new UserThread(user, "B");
UserThread p3 = new UserThread(user, "C");
// 스레드 스케줄링 : 우선순위 부여
p1.setPriority(p1.MAX_PRIORITY);
p2.setPriority(p2.NORM_PRIORITY);
p3.setPriority(p3.MIN_PRIORITY);
p1.start();
p2.start();
p3.start();
}
}
// 결과 => 순서 뒤죽박죽
A : 0번째 사용
B : 1번째 사용
C : 2번째 사용
A : 3번째 사용
C : 5번째 사용
B : 4번째 사용
C : 7번째 사용
A : 6번째 사용
B : 8번째 사용
- 동기화가 필요한 User 클래스의 add 메소드에 synchronized 키워드 적용한 경우
class User { private int userNo = 0; public synchronized void add(String name) { System.out.println(name + " : " + userNo++); } }
// 결과 A : 0번째 사용 B : 1번째 사용 C : 2번째 사용 A : 3번째 사용 B : 5번째 사용 C : 4번째 사용 A : 7번째 사용 B : 6번째 사용 C : 8번째 사용
4가지 케이스 비교
* I/O 멀티플렉싱: 하나의 통신 채널을 통해서 둘 이상의 데이터(시그널)를 전송하는데 사용되는 기술
위 그림처럼 총 4가지의 경우가 나올 수 있다. 이걸 좀 더 이해하기 쉽게 Case 별로 예시를 통해 보면서 이해하고 넘어가보자
상황 : 치킨집에 직접 치킨을 사러감
1) Blocking & Synchronous
나 : 대표님, 개발자 좀 더 뽑아주세요..
대표님 : 오케이, 잠깐만 거기 계세요!
나 : …?!!
대표님 : (채용 공고 등록.. 지원자 연락.. 면접 진행.. 연봉 협상..)
나 : (과정 지켜봄.. 궁금함.. 어차피 내 일 하러는 못 가고 계속 서 있음)
호출된 함수의 작업이 끝나야 제어권 돌려받고(Blocking) 호출한 함수는 호출된 함수가 결과값을 반환할 때까지 계속 기다린다.(Synchronous) => 요청과 동시에 결과값을 반환(즉, 그 자리에서 순차적으로 요청 => 결과값 반환을 하기 위해)
2) Blocking & Asynchronous
나 : 대표님, 개발자 좀 더 뽑아주세요..
대표님 : 오케이, 잠깐만 거기 계세요!
나 : …?!!
대표님 : (채용 공고 등록.. 지원자 연락.. 면접 진행.. 연봉 협상..)
나 : (안 궁금함.. 지나가는 말로 여쭈었는데 붙잡혀버림.. 딴 생각.. 못 가고 계속 서 있음)
Blocking-Async는 호출되는 함수가 바로 제어권을 반환하지 않고(Blocking), 호출한 함수는 작업 완료 여부를 신경쓰지 않는 것이다.(Async => 요청과 동시에 결과값을 반환하지 않아도 된다) 사실 Blocking-Sync와 성능적으로 차이가 거의 없다. 굳이 차이를 따지면 Callback을 넘겨주냐의 차이정도?
Blocking-Async는 별로 이점이 없어서 일부러 이 방식을 사용할 필요가 없기는 한데, 의도하지 않게 Blocking-Async로 동작하는 경우가 있다고 한다. 원래는 NonBlocking-Async를 추구하다가 의도와는 다르게 실제로는 Blocking-Async가 되어버리는 경우라고 하는데 그것은 바로 Node.js 에서 NonBlocking-Async를 사용하는데 DB 작업 호출 시에는 MySQL에서 제공하는 드라이버를 호출하게 되는데, 이 드라이버가 Blocking 방식이라고 한다.
이건 사실 Node.js 뿐아니라 Java의 JDBC도 마찬가지다. 다만 Node.js가 싱글 쓰레드 루프 기반이라 멀티 쓰레드 기반인 Java의 Servlet 컨테이너보다 문제가 더 두드러져 보일 뿐, Blocking-Async라는 근본 원인은 같다.
3) Non-blocking & Synchronous
나 : 대표님, 개발자 좀 더 뽑아주세요..
대표님 : 알겠습니다. 가서 볼 일 보세요.
나 : 넵!
대표님 : (채용 공고 등록.. 지원자 연락.. 면접 진행.. 연봉 협상..)
나 : 채용하셨나요?
대표님 : 아직요.
나 : 채용하셨나요?
대표님 : 아직요.
나 : 채용하셨나요?
대표님 : 아직요~!!!!!!
NonBlocking-Sync는 호출되는 함수는 바로 제어권을 리턴하고(NonBlocking), 호출하는 함수는 작업 완료 여부를 신경쓰는 것이다.(Sync =>요청과 동시에 (아직 결과가 나오지 않았다는)결과값을 반환한다.) 아직 결과가 나오지 않았다는 결과값을 받고 결과가 나올 때까지 신경쓰는 방법이 기다리거나 물어보거나 두 가지가 있는데, NonBlocking 함수를 호출했다면 제어권이 호출한 함수에게 주어져 기다릴 필요는 없고 자기 할 일 수행하면서 호출된 함수가 작업 완료되었는 지 물어보는 일이 남는다.
즉, 함수 호출 후 바로 반환 받아서 호출한 함수도 다른 작업을 할 수 있게 되지만(Blocking), 호출된 함수의 작업이 완료된 것은 아니며, 호출한 함수가 호출되는 함수 쪽에 작업 완료 여부를 계속 문의한다.(Synchronous)
4) Non-blocking & Asynchronous
나 : 대표님, 개발자 좀 더 뽑아주세요..
대표님 : 알겠습니다. 가서 볼 일 보세요.
나 : 넵!
대표님 : (채용 공고 등록.. 지원자 연락.. 면접 진행.. 연봉 협상..)
나 : (열일중..)
대표님 : 한 분 모시기로 했습니다~!
나 : 😍
제어권은 바로 돌려준다.(Non-Blocking) 호출한 함수는 작업완료 여부(결과값 반환)를 신경쓰지 않고 할 일을 한다.(Async) 결과는 호출된 함수가 callback을 호출(실행)하며 알아서 알려준다.
대표적인 예가 Node.js이다.

동기 & 비동기 & 스레드 & 멀티스레드
동기와 비동기 개념은 스레드와 멀티스레드 개념과도 같이 사용되는데 차이점이 무엇일까? (제가 이해한 내용으로 쓴 글이니 잘못된 정보가 있다면 댓글에 알려주시면 감사하겠습니다!)
- 싱글 스레드 - 동기
- 싱글 스레드 - 비동기
- 멀티 스레드 - 동기
- 멀티 스레드 - 비동기
위 4가지 경우에 대해 살펴보자.
- 싱글 스레드 - 동기: 작업의 요청과 결과가 동시에 일어난다. 이 작업을 다루는 스레드(공간의 개념으로 보자)는 한 개이다.
- 스레드 하나가 여러 작업을 처리한다.
- Thread_1 : |< — — A — →||< — — B — →||< — — C — →|
- 싱글스레드-비동기: 스레드 하나가 여러 작업을 처리하고 작업의 요청과 결과가 동시에 일어나지 않는다.
- Thread_1 : |< — — A |< — — B — |< — — C — | — A — | — C | — B — | B — → | C — → | — A — → |
- 멀티스레드-동기: 작업의 요청과 결과가 동시에 일어난다. 스레드를 요청 수 만큼 만든다. (아래는 동시에 실행된다는 것이 아니라 각 스레드가 맡는 작업을 보여주는 것이다. CPU 단일 코어라고 할 때 실제로는 동시에 실행되지 않고 빠르게 스레드를 번갈아며 실행해서 동시에 실행되는 것처럼 보인다. - 동시성)
- Thread_1 : |< — — A — →|
- Thread_2 : |< — — B — →|
- Thread_3 : |< — — C — →|
- 멀티스레드-비동기: 작업의 요청과 결과가 동시에 일어나지 않는다. 비동기 방식의 스레드가 여러개이다.(마찬가지로 동시에 실행된다는 것이 아닌 각 스레드가 맡는 작업으로 보여준 것이다.)
- Thread_1 : |< — — A |< — — B — | — A — | B — → | — A — → |
- Thread_2 : |< — — C |< — — D — | — C — | D — → | — C — → |
- Thread_3 : |< — — F |< — — E — | — F — | E — → | — F — → |
참고:
http://homoefficio.github.io/2017/02/19/Blocking-NonBlocking-Synchronous-Asynchronous/
Blocking-NonBlocking-Synchronous-Asynchronous
꽤 자주 접하는 용어다. 특히나 요즘들어 더 자주 접하게 되는데, 얼추 알고는 있고 알고 있는게 틀린 것도 아니지만, 막상 명확하게 구분해서 설명하라면 또 만만치가 않은.. 그래서 찾아보면
homoefficio.github.io
GitHub - gyoogle/tech-interview-for-developer: 👶🏻 신입 개발자 전공 지식 & 기술 면접 백과사전 📖
👶🏻 신입 개발자 전공 지식 & 기술 면접 백과사전 📖. Contribute to gyoogle/tech-interview-for-developer development by creating an account on GitHub.
github.com
https://musma.github.io/2019/04/17/blocking-and-synchronous.html
동기와 비동기, 그리고 블럭과 넌블럭
무릇 모든 개발자들, 아니 비단 개발자가 아니라 할지라도 컴퓨터 관련 산업계 종사자들이라면 오다 가다 한 번씩은 꼭 들어보고, 또 익혔음직한 내용이겠습니다. 이름하야, 동기(Synchronous)와 비
musma.github.io
GitHub - WooVictory/Ready-For-Tech-Interview: 💻 신입 개발자로서 준비를 하기 위해 지식을 정리하는 공간
💻 신입 개발자로서 준비를 하기 위해 지식을 정리하는 공간 👨💻. Contribute to WooVictory/Ready-For-Tech-Interview development by creating an account on GitHub.
github.com
GitHub - gyoogle/tech-interview-for-developer: 👶🏻 신입 개발자 전공 지식 & 기술 면접 백과사전 📖
👶🏻 신입 개발자 전공 지식 & 기술 면접 백과사전 📖. Contribute to gyoogle/tech-interview-for-developer development by creating an account on GitHub.
github.com
블로킹 Vs. 논블로킹, 동기 Vs. 비동기
와 드디어 이해했다 속이 후련~
velog.io
블로킹(Blocking)/논블로킹(Non-Blocking), 동기(Sync)/비동기(Async) 구분하기
보통 동기 = 블로킹을 같은 개념으로, 비동기 = 논블로킹을 같은 개념으로 헷갈리는 경우가 많다. 하지만 두 개념은 각각을 구분짓는 기준이 전혀 다르다. 블로킹/논블로킹은 한 작업이 처리되는
joooing.tistory.com
동기? 비동기? 쓰레드? 멀티 쓰레드?
저번 시간엔 멀티 쓰레드( https://jcchu.medium.com/%EB%A9%80%ED%8B%B0-%EC%93%B0%EB%A0%88%EB%93%9C%EB%9E%80-323bec5b450d )에 대해 알아봤습니다.
jcchu.medium.com
https://www.youtube.com/watch?v=IdpkfygWIMk
https://hamait.tistory.com/694
동기,비동기,단일쓰레드,멀티쓰레드 통신
자! 여기 우체국이 있습니다. ( 우체국 내부(OS) 는 알 필요 없고 외부 직원은 싱글쓰레드, 손님은 개별 유저라고 봅시다. ) 1. 싱글쓰레드 - 동기 우체국 하나가 여러 손님을 처리한다고 생각해 봅
hamait.tistory.com
'CS > 네트워크' 카테고리의 다른 글
HTTPS & SSL (0) | 2022.11.24 |
---|---|
HTTP (0) | 2022.11.01 |
OSI 7계층 VS TCP/IP 4계층 (0) | 2022.10.27 |
TCP VS UDP (0) | 2022.10.19 |
TCP의 3 way handshake와 4 way handshake (0) | 2022.10.14 |