스레드는 Python에서 어떻게 작동하며 일반적인 Python- 스레딩 관련 함정은 무엇입니까?


85

나는 스레드가 파이썬에서 어떻게 작동하는지에 대해 머리를 감싸려고 노력해 왔고, 스레드가 어떻게 작동하는지에 대한 좋은 정보를 찾기가 어렵습니다. 링크 나 무언가가 누락되었을 수도 있지만 공식 문서가 주제에 대해 그다지 철저하지 않은 것 같고 좋은 글을 찾을 수 없었습니다.

내가 알 수 있듯이 한 번에 하나의 스레드 만 실행할 수 있으며 활성 스레드는 10 개 정도의 명령마다 전환합니까?

좋은 설명이 어디에 있습니까? 아니면 설명 할 수 있습니까? 파이썬에서 스레드를 사용하는 동안 발생하는 일반적인 문제를 인식하는 것도 매우 좋습니다.

답변:


50

예, GIL (Global Interpreter Lock) 때문에 한 번에 하나의 스레드 만 실행할 수 있습니다. 이에 대한 몇 가지 통찰력이있는 링크는 다음과 같습니다.

마지막 링크에서 흥미로운 인용문 :

그게 무슨 뜻인지 설명하겠습니다. 스레드는 동일한 가상 머신 내에서 실행되므로 동일한 물리적 머신에서 실행됩니다. 프로세스는 동일한 물리적 시스템 또는 다른 물리적 시스템에서 실행될 수 있습니다. 스레드를 중심으로 애플리케이션을 설계하는 경우 여러 머신에 액세스하기 위해 아무 작업도하지 않았습니다. 따라서 단일 머신에있는 코어 수만큼 확장 할 수 있지만 (시간이 지남에 따라 상당 수) 실제로 웹 스케일에 도달하려면 어쨌든 여러 머신 문제를 해결해야합니다.

멀티 코어를 사용하려는 경우 pyprocessing 은 실제 병렬화를 수행하기 위해 프로세스 기반 API를 정의합니다. PEP는 또한 몇 가지 흥미로운 벤치 마크가 포함되어 있습니다.


1
smoothspan 인용문에 대한 정말 댓글입니다. Python 스레딩은 머신에 여러 개의 코어가 있어도 효과적으로 하나의 코어로 제한합니까? 다음 스레드가 컨텍스트 전환없이 이동할 준비가 될 수 있으므로 멀티 코어의 이점이있을 수 있지만 Python 스레드는 한 번에 1 개 이상의 코어를 사용할 수 없습니다.
James Brady

2
맞습니다. C 모듈이 GIL과 잘 상호 작용하고 자체 네이티브 스레드를 실행하지 않는 한, 파이썬 스레드는 사실상 하나의 코어로 제한됩니다.
Arafangion

실제로 다중 코어는 각 스레드가 GIL에 액세스 할 수 있는지 확인하는 데 많은 변동이 있기 때문에 스레드의 효율성을 떨어 뜨립니다. 새로운 GIL을 사용하더라도 성능은 여전히 ​​더 나쁩니다 ... dabeaz.com/python/NewGIL.pdf
Basic

2
모든 통역사에게 적용되지 않는 GIL 고려 사항에 유의하십시오. 내가 아는 한 IronPython과 Jython은 GIL없이 작동하므로 코드가 다중 프로세서 하드웨어를보다 효과적으로 사용할 수 있습니다. Arafangion이 언급했듯이 CPython 인터프리터는 Python 데이터 항목에 액세스 할 필요가없는 코드가 잠금을 해제 한 다음 반환하기 전에 다시 획득하는 경우 적절하게 다중 스레드로 실행될 수 있습니다.
holdenweb 2014

Python의 스레드간에 컨텍스트 전환이 발생하는 원인은 무엇입니까? 타이머 인터럽트를 기반으로합니까? 차단 또는 특정 수익률 호출?
CMCDragonkai

36

Python은 스레드하기가 매우 쉬운 언어이지만주의 할 점이 있습니다. 알아야 할 가장 큰 것은 Global Interpreter Lock입니다. 이렇게하면 하나의 스레드 만 인터프리터에 액세스 할 수 있습니다. 이것은 두 가지를 의미합니다. 1) 파이썬에서 lock 문을 거의 사용하지 않는 경우 2) 다중 프로세서 시스템을 이용하려면 별도의 프로세스를 사용해야합니다. 편집 : 또한 GIL을 둘러보고 싶다면 C / C ++에 코드 중 일부를 넣을 수 있다는 점도 지적해야합니다.

따라서 스레드를 사용하려는 이유를 다시 고려해야합니다. 듀얼 코어 아키텍처를 활용하기 위해 앱을 병렬화하려면 앱을 여러 프로세스로 분할하는 것을 고려해야합니다.

응답 성을 향상 시키려면 스레드 사용을 고려해야합니다. 그러나 다른 대안, 즉 microthreading이 있습니다. 살펴 봐야 할 몇 가지 프레임 워크도 있습니다.


@JS-고정. 어쨌든 그 목록은 오래되었습니다.
Jason Baker

멀티 코어 시스템을 활용하기 위해 수반되는 모든 오버 헤드와 함께 여러 프로세스가 필요하다는 사실이 제 생각에는 틀 렸습니다. 32 개의 논리 코어가있는 서버가 몇 개 있는데이를 효율적으로 사용하려면 32 개의 프로세스가 필요합니까? Madness
Basic

@Basic-요즘 프로세스 시작과 스레드 시작의 오버 헤드는 최소화됩니다. 초당 수천 개의 쿼리에 대해 이야기하면 문제가 발생할 수 있다고 생각하지만 처음에는 이러한 바쁜 서비스를 위해 Python을 선택하는 것이 문제입니다.
Jason Baker

20

다음은 기본 스레딩 샘플입니다. 20 개의 스레드를 생성합니다. 각 스레드는 스레드 번호를 출력합니다. 그것을 실행하고 그들이 인쇄하는 순서를 관찰하십시오.

import threading
class Foo (threading.Thread):
    def __init__(self,x):
        self.__x = x
        threading.Thread.__init__(self)
    def run (self):
          print str(self.__x)

for x in xrange(20):
    Foo(x).start()

당신이 암시했듯이 파이썬 스레드는 시간 분할을 통해 구현됩니다. 이것이 그들이 "병렬"효과를 얻는 방법입니다.

내 예제에서 내 Foo 클래스는 스레드를 확장 한 다음 스레드에서 실행 run하려는 코드가있는 메서드 를 구현합니다 . 스레드를 시작하려면 start()자동으로 run메서드를 호출하는 스레드 개체에서 호출합니다 .

물론 이것은 기본에 불과합니다. 결국 스레드 동기화 및 메시지 전달을위한 세마포어, 뮤텍스 및 잠금에 대해 배우고 싶을 것입니다.


10

개별 작업자가 I / O 바인딩 작업을 수행하는 경우 Python에서 스레드를 사용합니다. 머신의 여러 코어에서 확장하려는 경우 Python에 적합한 IPC 프레임 워크를 찾 거나 다른 언어를 선택하십시오.


4

참고 : 내가 언급하는 곳 thread은 명시 적으로 언급하기 전까지 는 특별히 파이썬 스레드를 의미합니다 .

스레드는 C/C++백그라운드 에서 온 경우 Python에서 약간 다르게 작동합니다 . 파이썬에서는 주어진 시간에 하나의 스레드 만 실행 상태 일 수 있습니다. 즉, 파이썬의 스레드는 설계 상 여러 코어에서 병렬로 스레드를 실행할 수 없기 때문에 여러 처리 코어의 성능을 진정으로 활용할 수 없습니다.

파이썬 메모리 관리 스레드 세이프되지 않기 때문에 각각의 스레드 파이썬 interpreter.This의 데이터 구조에 대한 단독 액세스 배타적 액세스라고 불리는기구에 의해 취득 요구 (글로벌 interpretr 고정) .GIL

Why does python use GIL?

여러 스레드가 인터프리터 상태에 동시에 액세스하여 인터프리터 상태를 손상시키는 것을 방지하기 위해.

아이디어는 스레드가 실행될 때마다 (메인 스레드 인 경우에도) GIL을 획득하고 미리 정의 된 시간 간격 후에 GIL을 현재 스레드에서 해제하고 다른 스레드 (있는 경우)에 의해 다시 획득하는 것입니다.

Why not simply remove GIL?

GIL을 제거하는 것이 불가능한 것은 아닙니다. 그렇게하는 대신에 액세스를 직렬화하기 위해 인터프리터 내부에 여러 개의 잠금을 두게되어 단일 스레드 응용 프로그램의 성능이 저하됩니다.

따라서 GIL을 제거하는 비용은 결코 바람직하지 않은 단일 스레드 응용 프로그램의 성능 저하로 보상됩니다.

So when does thread switching occurs in python?

스레드 전환은 GIL이 해제 될 때 발생하는데, GIL은 언제 해제됩니까? 고려해야 할 두 가지 시나리오가 있습니다.

Thread가 CPU Bound 작업을 수행하는 경우 (Ex 이미지 처리).

이전 버전의 python에서 스레드 전환은 고정 된 Python 명령어 수 이후에 발생 100했지만 기본적으로으로 설정되어 있습니다 . 밀리 초에서 1 초까지 매우 100거칠 수 있으므로 실행하는 데 걸리는 시간에 관계없이 모든 명령 후에 GIL을 릴리스 하는 것은 좋지 않은 정책입니다.

새 버전에서는 명령어 개수를 측정 항목으로 사용하여 thread를 전환하는 대신 구성 가능한 시간 간격이 사용됩니다. 기본 전환 간격은 5 밀리 초이며을 사용하여 현재 전환 간격을 얻을 수 있습니다 sys.getswitchinterval(). 이것은 다음을 사용하여 변경할 수 있습니다.sys.setswitchinterval()

스레드가 일부 IO 바인딩 작업을 수행하는 경우 (Ex 파일 시스템 액세스 또는
네트워크 IO)

GIL은 스레드가 IO 작업이 완료 될 때까지 대기 할 때마다 해제됩니다.

Which thread to switch to next?

인터프리터에는 자체 스케줄러가 없습니다. 간격이 끝날 때 스케줄되는 스레드는 운영 체제의 결정입니다. .


3

GIL에 대한 한 가지 쉬운 솔루션은 다중 처리 모듈입니다. 스레딩 모듈을 대체하는 드롭 인으로 사용할 수 있지만 스레드 대신 여러 인터프리터 프로세스를 사용합니다. 이 때문에 단순한 작업을위한 일반 스레딩보다 약간 더 많은 오버 헤드가 있지만 필요한 경우 실제 병렬화의 이점을 제공합니다. 또한 여러 물리적 머신으로 쉽게 확장됩니다.

내가 더 살펴볼 것보다 진정으로 대규모 병렬화가 필요하지만 더 포괄적 인 프레임 워크를 구현하는 데 필요한 모든 작업없이 한 컴퓨터의 모든 코어 또는 몇 가지 다른 코어로 확장하려는 경우 이보다 더 적합합니다. .


2

GIL은 여러 작업의 모양을 보여주기 위해 자주 폴링하도록 설정되어 있음을 기억하십시오. 이 설정은 미세 조정할 수 있지만 스레드가 수행하는 작업이 있거나 많은 컨텍스트 전환이 문제를 일으킬 것이라는 제안을 제공합니다.

나는 프로세서에 여러 부모를 제안하고 동일한 코어에서 같은 작업을 유지하려고 노력할 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.