logo

Python에서 GIL이란 무엇입니까? 전역 통역사 잠금

이 튜토리얼은 Python의 중요한 주제 중 하나인 GIL에 중점을 둘 것입니다. 또한 GIL이 코드 구현을 통해 Python 프로그램 성능에 어떤 영향을 미치는지 다룰 것입니다. 이 주제를 살펴보기 전에 GIL에 대한 기본 아이디어를 살펴보겠습니다.

GIL 또는 전역 통역사 잠금

Python Global Interpreter Lock(GIL)은 멀티스레딩 프로그래밍의 중요한 부분입니다. 여러 프로세스로 작업할 때 사용되는 프로세스 잠금 유형입니다. 하나의 스레드에만 제어권을 부여합니다. 일반적으로 Python은 단일 스레드를 사용하여 단일 프로세스를 실행합니다. GIL을 사용하면 단일 스레드 및 다중 스레드 프로세스에서 동일한 성능 결과를 얻을 수 있습니다. 이는 스레드를 방지하고 단일 스레드로 작동하기 때문에 Python에서 멀티스레딩 달성을 제한합니다.

참고 - Python은 스레딩 패키지가 다중 CPU 코어를 사용할 수 없기 때문에 다중 스레딩을 지원하지 않습니다.

Python 개발자가 GIL을 사용하는 이유는 무엇입니까?

Python은 메모리 관리에 사용되는 고유한 참조 카운터 기능을 제공합니다. 참조 카운터는 데이터 객체에 값을 할당하기 위해 Python 내부에서 만들어진 총 참조 수를 계산합니다. 참조 횟수가 0에 도달하면 개체에 할당된 메모리가 해제됩니다. 아래 예를 살펴보겠습니다.

예 -

 import sys a = [] b = a sys.getrefcount(a) 

참조 카운트 변수의 주요 관심사는 두 개 또는 세 개의 스레드가 해당 값을 동시에 늘리거나 줄이려고 할 때 영향을 받을 수 있다는 것입니다. 이를 경쟁 조건이라고 합니다. 이 조건이 발생하면 해제되지 않는 메모리 누수가 발생할 수 있습니다. Python 프로그램에서 충돌이나 버그가 발생할 수 있습니다.

GIL은 스레드 전체에 걸쳐 모든 공유 데이터 구조에 대한 잠금을 사용하여 일관성 없이 변경되지 않도록 하여 이러한 상황을 제거하는 데 도움이 됩니다. Python은 스레드로부터 안전한 메모리 관리를 다루기 때문에 GIL을 구현하는 쉬운 방법을 제공합니다. GIL은 Python에서 처리하기 위해 스레드에 단일 잠금을 제공해야 합니다. 하나의 잠금만 처리하면 되므로 단일 스레드 프로그램의 성능이 향상됩니다. 또한 CPU 바인딩된 프로그램을 만드는 데 도움이 되며 교착 상태를 방지합니다.

파이썬 프로그램 목록

다중 스레드 Python 프로그램에 미치는 영향

성능의 CPU 경계와 일반적인 Python 프로그램 또는 컴퓨터 프로그램의 I/O 경계에는 차이가 있습니다. CPU 바인딩된 프로그램은 일반적으로 CPU를 한계까지 밀어붙입니다. 이러한 프로그램은 일반적으로 행렬 곱셈, 검색, 이미지 처리 등과 같은 수학적 계산에 사용됩니다.

I/O 바운드 프로그램은 사용자, 파일, 데이터베이스, 네트워크 등에 의해 생성될 수 있는 입력/출력을 얻는 데 시간을 소비하는 프로그램입니다. 이러한 프로그램은 소스가 입력을 제공할 때까지 상당한 시간을 기다려야 합니다. 반면 소스에도 자체 처리 시간이 있습니다. 예를 들어 사용자는 입력으로 무엇을 입력할지 고민하고 있습니다.

다음 예를 이해해 봅시다.

int를 문자열로 변환 java

예 -

 import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 start_time = time.time() countdown(COUNT) end_time = time.time() print('Time taken in seconds -', end_time - start_time) 

산출:

 Time taken in seconds - 7.422671556472778 

이제 두 스레드를 실행하여 위 코드를 수정합니다.

예 - 2:

 import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 thread1 = Thread(target=countdown, args=(COUNT//2,)) thread2 = Thread(target=countdown, args=(COUNT//2,)) start_time = time.time() thread1.start() thread2.start() thread1.join() thread2.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time) 

산출:

 Time taken in seconds - 6.90830135345459 

보시다시피 두 코드 모두 완료하는 데 동일한 시간이 걸렸습니다. GIL은 CPU 바인딩된 스레드가 두 번째 코드에서 병렬로 실행되는 것을 방지했습니다.

GIL이 아직 제거되지 않은 이유는 무엇입니까?

많은 프로그래머들이 이에 대해 불만을 갖고 있지만 Python은 GIL 제거만큼 중요한 변경 사항을 가져올 수 없습니다. 또 다른 이유는 현재 GIL이 개선되지 않았기 때문입니다. Python 3에서 변경되면 몇 가지 심각한 문제가 발생합니다. GIL을 제거하는 대신 GIL 개념을 개선할 수 있습니다. 귀도 반 로솜(Guido van Rossom)에 따르면 -

'단일 스레드 프로그램(및 다중 스레드이지만 I/O 바인딩된 프로그램)의 성능이 저하되지 않는 경우에만 Py3k에 일련의 패치를 적용하는 것을 환영합니다.'

우선순위 큐 자바

GIL로 해결된 동일한 문제를 해결할 수 있는 방법도 많이 있지만 구현하기가 어렵습니다.

Python의 GIL을 다루는 방법

다중 처리를 사용하는 것은 프로그램이 GIL로부터 보호되는 가장 적합한 방법입니다. Python은 실행할 각 프로세스에 대해 다양한 인터프리터를 제공하므로 해당 시나리오에서는 다중 처리의 각 프로세스에 단일 스레드가 제공됩니다. 다음 예를 이해해 봅시다.

예 -

 from multiprocessing import Pool import time COUNT = 50000000 def countdown(num): while num>0: num -= 1 if __name__ == '__main__': pool = Pool(processes=2) start_time = time.time() r1 = pool.apply_async(countdown, [COUNT//2]) r2 = pool.apply_async(countdown, [COUNT//2]) pool.close() pool.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time) 

산출:

 Time taken in seconds - 3.3707828521728516 

괜찮은 성능이 향상되는 것처럼 보일 수 있지만 프로세스 관리에는 고유한 오버헤드가 있고 여러 프로세스가 여러 스레드보다 무겁습니다.

결론

이 튜토리얼에서는 GIL과 이를 사용하는 방법에 대해 논의했습니다. 단일 스레드에 제어권을 부여하여 한 번에 실행할 수 있습니다. 이 튜토리얼에서는 Python 프로그래머에게 GIL이 중요한 이유도 다루었습니다.