import select
import socket
def slow_systemcall():
select.select([socket.socket()], [], [], 0.1)
start = time.time()
for _ in range(5):
slow_systemcall()
end = time.time()
delta = end - start
print(f'Took {delta:.3f} seconds')
# Took 0.526 seconds
import select
import socket
def slow_systemcall():
select.select([socket.socket()], [], [], 0.1)
start = time.time()
threads = []
for _ in range(5):
thread = Thread(target=slow_systemcall)
thread.start()
threads.append(thread)
# Example 9
def compute_helicopter_location(index):
pass
for i in range(5):
compute_helicopter_location(i)
for thread in threads:
thread.join()
end = time.time()
delta = end - start
print(f'Took {delta:.3f} seconds')
# Took 0.103 seconds
여러분들은 위의 두 코드를 보고 속도의 차이점을 명확히 설명할 수 있나요?
만약 단순히 쓰레드 라는 특성만이라고 한다면 아직 완벽한 대답이 될 수 없을 것 입니다.
쓰레드 때문이라고 생각하시는 분들은 GIL(Global Interpreter Lock)를 덧붙여 설명해주실 수 있나요?
만일 GIL를 설명하였다면 GIL 특성으로 멀티쓰레드 중 단 하나의 쓰레드만 활성시킬 수 있음을 알고 있다는 것인데,
그렇다면 어떻게 아래의 코드는 위의 코드보다 더 빠른 실행결과를 보여줄 수 있었던 것일까요?
GIL에 의하여 쓰레드는 단 하나의 쓰레드로 독점이 될것이고 그 전제가 맞다면 위와 동일한 속도를 보여줘야 할텐데요.
그 이유를 이해하기 위해선 slow_systemcall() 의 내부코드가 시스템 콜에 의한 I/O작업이 되고 있음을 알야아 합니다.
파이썬 스레드는 여러 시스템 콜을 병렬로 처리할 수 있는 기능을 가지고 있습니다.
그럼으로 I/O 작업들을 병행하여 처리할 수 있다는 뜻이 되겠죠. 좀 더 구체적으로 쓰레드가 slow_systemcall() 를 실행합니다.
해당 내부기능에 I/O 작업을 발견하고, GIL를 해제합니다.
그리고 다음 쓰레드를 실행하고, 다시 해제하는 것을 반복하여 병렬로 실행히 가능하게 된 것이죠.
'프로그래밍 언어 > Python' 카테고리의 다른 글
파이썬에서 외부 터미널 명령어 사용하기 ( subprocess ) (0) | 2023.09.19 |
---|---|
GIL를 무조건 신뢰하면 안되는 이유( feat.스레드 감수성 ) (0) | 2023.09.15 |
클래스를 디버깅해보자(feat.메타클래스,클래스 데코레이터) (0) | 2023.09.15 |
디스크립터 __set_name__ 의 활용방법 (0) | 2023.09.14 |
메타클래스와 init_subclass (0) | 2023.09.12 |