동기/비동기, 블로킹/논블로킹

Junha Kim·2021년 4월 25일
1

FastAPI를 하면서 파이썬의 비동기 프로그래밍을 접해보았다. 하지만 정확한 이해 없이 마구잡이로 async를 하니, 속도가 오히려 떨어지는 상황을 겪게되는 경우가 있었다.

이러한 사태를 다시 겪지 않기 위해 기초부터 다시 찾아보기로 했다.

동기/비동기 vs 블로킹/논블로킹

우선 둘의 차이점을 먼저 말하고 가는 것이 뒷부분을 이해하는데 편할 것 같다.

동기/비동기

  • 작업하는 주체의 관점
  • 작업의 완료 여부에 따라

블로킹/논블로킹

  • 여러 작업의 흐름 관점
  • 1개의 작업이 다른 작업의 실행을 막는지 여부

동기 vs 비동기

작업을 수행하는 2개 이상의 주체가 시작, 종료 시간을 서로 맞출 것인지?

동기

  • 작업 요청을 했을 때 요청의 결과값(return)을 직접 받는 것이다

    def a():
    	# do something
    	return 1
    
    def b():
    	# do something
    	return 2
    
    def c():
    	# do something
    	return 3
    
    def d():
    	# do something
    	return 4
    
    def main():
    	print(a())
    
    	print(b())
    
    	print(c())
    
    	print(d())
    
    # 1 2 3 4
    • main 함수에서 불러진 a, b, c, d의 함수의 리턴이 될때까지 기다렸다가 print문이 실행된다.
  • 호출한 함수가 작업 완료를 신경 쓴다. → 작업이 끝날때까지 기다린다.

비동기

  • 작업 요청을 했을 때 요청의 결과값(return)을 간접적으로 받는 것이다.
  • 위의 코드를 비동기로 실행한다면 print되는 순서를 보장할 수 없다 → 일찍 끝나는 것이 먼저 실행된다.
  • 호출된 함수(callback 함수)가 작업 완료를 신경 쓴다. → 작업을 줘놓고 끝나면 알려달라

이 2개를 유머로 표현한 것이 있다.

컵라면을 만드는 함수가 있다고 했을때, 7개를 순차적으로 쓰고 실행을 한다면 동기적으로 실행되어 7개의 컵라면이 모두 완성될때까지 21분이 걸리는 것이다.

def make_ramyeon():
	# wait 3 minutes
	return ramyeon

def main():
	results = []
	for _ in range(7):
		results.append(make_ramyeon())
	return results

하지만 비동기로 실행했을 때는 각 함수들이 동시에 실행되어 약 3분이면 모든 작업이 끝나게 되는 것이다.

블로킹 VS 논블로킹

실행 제어권을 돌려주는가? 안돌려주는가?

블로킹

  • 함수1 내부에서 함수2를 실행했을 때, 함수2의 모든 실행이 끝나기 전까지 실행 제어권을 함수1에게 다시 주지 않는다.
  • 요청한 작업을 마칠 때까지 계속 대기한다.
  • Thread 관점으로 본다면, 요청한 작업을 마칠 때까지 계속 대기하며 return 값을 받을 때까지 한 Thread를 계속 사용/대기 한다.

논블로팅

  • 함수1 내부에서 함수2를 실행하는데, 함수2가 완료되기 전에 함수1에게 실행 제어권을 넘겨주어 함수1의 다음 코드를 실행할 수 있다.
  • 요청한 작업을 즉시 마칠 수 없다면 즉시 return한다.
  • Thread 관점으로 본다면, 하나의 Thread가 여러 개의 IO를 처리 가능하다.

정리

이렇게 봐도 뭔가 비슷해 보인다. 하지만 관점이 다르다는 것을 명심해야한다.

blocking/non-blocking

호출되는 함수가 바로 return 하는지 (제어권을 넘기는가)가 관심사이다

동기/비동기

호출되는 함수의 작업 완료 여부(기다리는가)를 누가 신경쓰느냐가 관심사이다.

  • 호출되는 함수로부터 바로 return 받더라도 작업 완료 여부를 호출한 함수 스스로 확인하며 신경 쓴다면 동기이다.

0개의 댓글