수색…


비고

GIL이있는 이유는 무엇입니까?

GIL은 1992 년 Python 스레드가 처음 시작된 이래 CPython에서 사용되어 왔습니다. Python 코드를 실행하는 스레드의 안전을 보장하기 위해 설계되었습니다. GIL로 작성된 파이썬 인터프리터는 다중 네이티브 스레드가 파이썬 바이트 코드를 동시에 실행하는 것을 방지합니다. 이렇게하면 플러그인이 코드가 스레드로부터 안전하다는 것을 쉽게 확인할 수 있습니다. 단순히 GIL을 잠그면 활성 스레드 만 실행할 수 있으므로 코드는 자동으로 스레드로부터 안전합니다.

짧은 버전 : GIL은 얼마나 많은 프로세서와 스레드가 있더라도 한 번에 하나의 파이썬 인터프리터 스레드 만 실행되도록합니다.

이것에는 많은 사용 편의 혜택이 있지만 또한 많은 부정적인 이점이 있습니다.

GIL은 파이썬 언어의 요구 사항이 아닙니다. 따라서 표준 Python 코드에서 GIL에 직접 액세스 할 수 없습니다. 파이썬의 모든 구현이 GIL을 사용하는 것은 아닙니다.

GIL을 가진 해석기 : CPython, PyPy, Cython (하지만 nogilnogil 비활성화 할 수 있습니다)

GIL이없는 인터프리터 : Jython, IronPython

GIL 작동 방식에 대한 세부 정보 :

스레드가 실행 중이면 GIL을 잠급니다. 스레드가 실행되기를 원하면 GIL을 요청하고 사용 가능할 때까지 기다립니다. CPython에서 버전 3.2 이전에는 실행중인 스레드가 특정 수의 파이썬 명령어를 확인하여 다른 코드가 잠금을 원했는지 확인했습니다 (즉, 잠금을 해제 한 다음 다시 요청했습니다). 이 메소드는 스레드가 기아를 일으키는 경향이있었습니다. 주로 잠금을 해제 한 스레드가 대기 스레드가 깨어날 수 있기 전에 다시 획득하기 때문입니다. 3.2 이후, GIL을 원하는 스레드는 잠시 동안 잠금을 대기하고 그 시간이 지나면 실행중인 스레드가 강제로 공유하는 변수를 설정합니다. 하지만 여전히 실행 시간이 상당히 길어질 수 있습니다. 자세한 내용은 dabeaz.com (참조 절 참조)에서 아래 링크를 참조하십시오.

스레드가 I / O 작업을 수행하면 CPython은 자동으로 GIL을 해제합니다. 이미지 처리 라이브러리와 numpy number crunching 작업은 처리하기 전에 GIL을 해제합니다.

GIL의 이점

GIL을 사용하는 통역사의 경우 GIL은 전신입니다. 응용 프로그램의 상태를 유지하는 데 사용됩니다. 이점은 다음과 같습니다.
  • 가비지 수집 - 스레드 안전 참조 횟수는 GIL이 잠긴 동안 수정되어야합니다. CPython에서 모든 garbarge 컬렉션은 GIL에 묶여 있습니다. 이것은 큰 것이다. GIL에 관한 python.org wiki 기사 (아래 참조)를 참조하여 GIL을 제거하려는 경우 여전히 기능적이어야하는 항목에 대한 세부 사항을 참조하십시오.
  • GIL을 다루는 프로그래머가 쉽게 - 모든 것을 잠그는 것은 간단하지만 코드 작성이 쉽습니다.
  • 다른 언어에서 모듈 가져 오기가 쉬움

GIL의 결과

GIL은 한 스레드가 파이썬 인터프리터 내에서 파이썬 코드를 한 번에 하나씩 만 실행할 수있게합니다. 즉 엄격한 파이썬 코드를 실행하는 프로세스의 멀티 스레딩은 작동하지 않습니다. GIL에 대해 스레드를 사용할 때 단일 스레드에서 실행했을 때보 다 스레드에서 성능이 떨어집니다.

참고 문헌 :

https://wiki.python.org/moin/GlobalInterpreterLock - 모든 기능에 대한 세부 정보

http://programmers.stackexchange.com/questions/186889/why-was-python-written-with-the-gil - 명확하게 작성된 요약

http://www.dabeaz.com/python/UnderstandingGIL.pdf - GIL의 작동 방식 및 여러 코어에서 느려지는 이유

http://www.dabeaz.com/GIL/gilvis/index.html - GIL이 스레드를 잠그는 방법을 보여주는 데이터 시각화

http://jeffknupp.com/blog/2012/03/31/pythons-hardest-problem/ - GIL 문제의 역사를 이해하기 쉽습니다.

https://jeffknupp.com/blog/2013/06/30/pythons-hardest-problem-revisited/ - GIL의 한계를 해결하는 방법에 대한 세부 정보

다중 처리 .Pool

파이썬에서 쓰레드를 사용하는 방법을 묻는 간단한 대답은 "하지 마라. 대신 프로세스를 사용하라"이다. 다중 처리 모듈을 사용하면 스레드 작성과 유사한 구문으로 프로세스를 작성할 수 있지만 편리한 Pool 오브젝트를 사용하는 것이 더 좋습니다.

David Beazley가 처음 GIL에 대한 스레드의 위험을 보여주기 위해 사용한 코드를 사용하여 다중 처리를 사용하여 다시 작성합니다 .Pool :

GIL 스레딩 문제를 보여준 David Beazley의 코드

from threading import Thread
import time
def countdown(n):
    while n > 0:
        n -= 1

COUNT = 10000000

t1 = Thread(target=countdown,args=(COUNT/2,))
t2 = Thread(target=countdown,args=(COUNT/2,))
start = time.time()
t1.start();t2.start()
t1.join();t2.join()
end = time.time()
print end-start
다중 처리를 사용하여 다시 작성되었습니다 .Pool :
import multiprocessing
import time
def countdown(n):
    while n > 0:
        n -= 1

COUNT = 10000000

start = time.time()
with multiprocessing.Pool as pool:
    pool.map(countdown, [COUNT/2, COUNT/2])

    pool.close()
    pool.join()

end = time.time()
print(end-start)

스레드를 만드는 대신 새로운 프로세스를 만듭니다. 각 프로세스는 자체 통역사이기 때문에 GIL 충돌이 없습니다. 위의 예제에서는 두 개만 필요하지만, 풀은 시스템에 코어가있는 프로세스만큼 많은 프로세스를 열 수 있습니다. 실제 시나리오에서는 시스템에 프로세서가있는 길이 이상으로 목록을 디자인하려고합니다. 풀은 생성 한 프로세스의 수까지 각 인수로 실행하는 기능을 실행합니다. 함수가 끝나면 목록의 나머지 함수는 해당 프로세스에서 실행됩니다.

나는 with 문을 사용해도 풀을 닫지 않고 풀면 프로세스가 계속 존재한다는 것을 알았다. 자원을 정리하기 위해 항상 수영장을 닫고 가입합니다.

Cython nogil :

Cython은 파이썬 인터프리터의 대안입니다. 그것은 GIL을 사용하지만, 당신이 그것을 사용할 수 없도록합니다. 설명서 보기

예를 들어 David Beazley가 처음 GIL에 대한 스레드의 위험을 표시하는 코드를 사용하여 nogil을 사용하여 다시 작성합니다.

GIL 스레딩 문제를 보여준 David Beazley의 코드

from threading import Thread
import time
def countdown(n):
    while n > 0:
        n -= 1

COUNT = 10000000

t1 = Thread(target=countdown,args=(COUNT/2,))
t2 = Thread(target=countdown,args=(COUNT/2,))
start = time.time()
t1.start();t2.start()
t1.join();t2.join()
end = time.time()
print end-start

nogil을 사용하여 다시 작성 (CYTHON에서만 작동) :

from threading import Thread
import time
def countdown(n):
    while n > 0:
        n -= 1

COUNT = 10000000

with nogil:
    t1 = Thread(target=countdown,args=(COUNT/2,))
    t2 = Thread(target=countdown,args=(COUNT/2,))
    start = time.time()
    t1.start();t2.start()
    t1.join();t2.join()
    
end = time.time()
print end-start

Cython을 사용하는 한 간단합니다. 문서에 따르면 파이썬 객체를 변경하지 말아야한다는 내용이 있습니다.

명령문의 본문에있는 코드는 어떤 식 으로든 파이썬 객체를 조작해서는 안되며, 먼저 GIL을 다시 획득하지 않고 파이썬 객체를 조작하는 것을 호출해서는 안됩니다. Cython은 현재 이것을 확인하지 않습니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow