Python의 동시성-빠른 가이드
이 장에서는 Python의 동시성 개념을 이해하고 다양한 스레드와 프로세스에 대해 알아 봅니다.
동시성이란 무엇입니까?
간단히 말해서 동시성은 두 개 이상의 이벤트가 동시에 발생하는 것입니다. 동시성은 주어진 시간에 많은 이벤트가 동시에 발생하기 때문에 자연스러운 현상입니다.
프로그래밍 측면에서 동시성은 실행에서 두 작업이 겹치는 경우입니다. 동시 프로그래밍을 사용하면 이전 요청이 완료 될 때까지 기다리지 않고 요청을 동시에 처리 할 수 있기 때문에 애플리케이션 및 소프트웨어 시스템의 성능을 향상시킬 수 있습니다.
동시성에 대한 기록 검토
다음 요점은 동시성에 대한 간략한 역사적 검토를 제공합니다.
철도의 개념에서
동시성은 철도의 개념과 밀접한 관련이 있습니다. 철도의 경우 모든 열차가 목적지까지 안전하게 도착할 수 있도록 동일한 철도 시스템에서 여러 열차를 처리 할 필요가있었습니다.
학계에서의 동시 컴퓨팅
컴퓨터 과학 동시성에 대한 관심은 1965 년 Edsger W. Dijkstra가 발표 한 연구 논문에서 시작되었습니다.이 논문에서 그는 동시성 제어의 속성 인 상호 배제 문제를 식별하고 해결했습니다.
높은 수준의 동시성 프리미티브
최근 프로그래머들은 높은 수준의 동시성 프리미티브의 도입으로 인해 향상된 동시 솔루션을 얻고 있습니다.
프로그래밍 언어와의 동시성 향상
Google의 Golang, Rust 및 Python과 같은 프로그래밍 언어는 더 나은 동시 솔루션을 얻는 데 도움이되는 분야에서 놀라운 발전을 이루었습니다.
스레드 및 멀티 스레딩이란 무엇입니까?
Thread운영 체제에서 수행 할 수있는 가장 작은 실행 단위입니다. 그 자체는 프로그램이 아니라 프로그램 내에서 실행됩니다. 즉, 스레드는 서로 독립적이지 않습니다. 각 스레드는 다른 스레드와 코드 섹션, 데이터 섹션 등을 공유합니다. 경량 프로세스라고도합니다.
스레드는 다음 구성 요소로 구성됩니다.
다음 실행 가능한 명령어의 주소로 구성된 프로그램 카운터
Stack
레지스터 세트
고유 ID
Multithreading반면에 여러 스레드를 동시에 실행하여 운영 체제 사용을 관리하는 CPU의 기능입니다. 멀티 스레딩의 주요 개념은 프로세스를 여러 스레드로 분할하여 병렬화를 달성하는 것입니다. 멀티 스레딩의 개념은 다음 예제를 통해 이해할 수 있습니다.
예
MS Word를 열어 콘텐츠를 입력하는 특정 프로세스를 실행한다고 가정 해 보겠습니다. MS Word를 열려면 스레드 하나가 할당되고 내용을 입력하려면 다른 스레드가 필요합니다. 이제 기존 항목을 편집하려면 편집 작업을 수행하는 데 다른 스레드가 필요합니다.
프로세스 및 다중 처리 란 무엇입니까?
ㅏprocess시스템에서 구현할 기본 작업 단위를 나타내는 엔티티로 정의됩니다. 간단히 말해서 컴퓨터 프로그램을 텍스트 파일로 작성하고이 프로그램을 실행하면 프로그램에서 언급 한 모든 작업을 수행하는 프로세스가됩니다. 프로세스 수명주기 동안 시작, 준비, 실행, 대기 및 종료와 같은 여러 단계를 거칩니다.
다음 다이어그램은 프로세스의 여러 단계를 보여줍니다.
프로세스에는 기본 스레드라고하는 하나의 스레드 만 있거나 자체 레지스터 세트, 프로그램 카운터 및 스택이있는 여러 스레드가있을 수 있습니다. 다음 다이어그램은 차이점을 보여줍니다.
Multiprocessing,반면에 단일 컴퓨터 시스템 내에서 두 개 이상의 CPU 장치를 사용하는 것입니다. 우리의 주요 목표는 하드웨어에서 최대한의 잠재력을 얻는 것입니다. 이를 위해서는 컴퓨터 시스템에서 사용할 수있는 전체 CPU 코어 수를 활용해야합니다. 다중 처리는이를위한 최선의 방법입니다.
Python은 가장 인기있는 프로그래밍 언어 중 하나입니다. 다음은 동시 애플리케이션에 적합한 몇 가지 이유입니다.
구문 설탕
Syntactic sugar는 사물을 더 쉽게 읽고 표현할 수 있도록 설계된 프로그래밍 언어 내의 구문입니다. 그것은 인간이 사용하기 위해 언어를 "더 달콤"하게 만듭니다. 사물을 더 명확하고 간결하게 표현하거나 선호도에 따라 다른 스타일로 표현할 수 있습니다. Python은 객체에 대해 작동하도록 정의 할 수있는 Magic 메서드와 함께 제공됩니다. 이러한 Magic 메서드는 구문 설탕으로 사용되며 더 이해하기 쉬운 키워드에 바인딩됩니다.
대규모 커뮤니티
Python 언어는 AI, 기계 학습, 딥 러닝 및 정량 분석 분야에서 일하는 데이터 과학자와 수학자 사이에서 엄청난 채택률을 보였습니다.
동시 프로그래밍에 유용한 API
Python 2와 3에는 병렬 / 동시 프로그래밍 전용 API가 많이 있습니다. 가장 인기있는 것은threading, concurrent.features, multiprocessing, asyncio, gevent and greenlets, 기타
동시 애플리케이션 구현시 Python의 한계
Python에는 동시 애플리케이션에 대한 제한이 있습니다. 이 제한을GIL (Global Interpreter Lock)Python 내에 있습니다. GIL은 결코 다중 CPU 코어를 사용하도록 허용하지 않으므로 Python에는 진정한 스레드가 없다고 말할 수 있습니다. 다음과 같이 GIL의 개념을 이해할 수 있습니다.
GIL (Global Interpreter Lock)
파이썬 세계에서 가장 논란이 많은 주제 중 하나입니다. CPython에서 GIL은 상호 배제 잠금 (mutex)으로 스레드를 안전하게 만듭니다. 즉, GIL이 여러 스레드가 Python 코드를 병렬로 실행하는 것을 방지한다고 말할 수 있습니다. 잠금은 한 번에 하나의 스레드 만 보유 할 수 있으며 스레드를 실행하려면 먼저 잠금을 획득해야합니다. 아래 다이어그램은 GIL의 작동을 이해하는 데 도움이됩니다.
그러나 Python에는 다음과 같은 일부 라이브러리 및 구현이 있습니다. Numpy, Jpython 과 IronPytbhon. 이러한 라이브러리는 GIL과의 상호 작용없이 작동합니다.
다중 스레드 프로그램과 관련하여 동시성과 병렬 처리가 모두 사용되지만 유사성과 차이점에 대해 많은 혼란이 있습니다. 이와 관련하여 큰 질문은 동시성 병렬 처리입니까? 두 용어가 매우 비슷해 보이지만 위의 질문에 대한 대답은 아니오이지만 동시성과 병렬성은 동일하지 않습니다. 자, 그들이 같지 않다면 그들 사이의 기본적인 차이점은 무엇입니까?
간단히 말해서 동시성은 서로 다른 스레드에서 공유 상태에 대한 액세스를 관리하는 것을 다루고 다른 쪽에서는 병렬 처리는 하드웨어 성능을 향상시키기 위해 여러 CPU 또는 해당 코어를 사용하는 것을 처리합니다.
세부적인 동시성
동시성은 실행에서 두 작업이 겹치는 경우입니다. 응용 프로그램이 동시에 둘 이상의 작업에서 진행되는 상황 일 수 있습니다. 우리는 그것을 도식적으로 이해할 수 있습니다. 다음과 같이 여러 작업이 동시에 진행되고 있습니다.
동시성 수준
이 섹션에서는 프로그래밍 측면에서 세 가지 중요한 동시성 수준에 대해 설명합니다.
낮은 수준의 동시성
이 수준의 동시성에는 원자 적 작업이 명시 적으로 사용됩니다. 오류가 발생하기 쉽고 디버깅이 어렵 기 때문에 이러한 종류의 동시성을 응용 프로그램 빌드에 사용할 수 없습니다. 파이썬조차도 그러한 종류의 동시성을 지원하지 않습니다.
중간 수준의 동시성
이 동시성에서는 명시 적 원자 연산이 사용되지 않습니다. 명시 적 잠금을 사용합니다. Python 및 기타 프로그래밍 언어는 이러한 종류의 동시성을 지원합니다. 대부분의 애플리케이션 프로그래머는이 동시성을 사용합니다.
높은 수준의 동시성
이 동시성에서는 명시 적 원자 작업이나 명시 적 잠금이 사용되지 않습니다. 파이썬은concurrent.futures 이러한 종류의 동시성을 지원하는 모듈.
동시 시스템의 속성
프로그램 또는 동시 시스템이 정확하려면 일부 속성이 충족되어야합니다. 시스템 종료와 관련된 속성은 다음과 같습니다.
정확성 속성
정확성 속성은 프로그램이나 시스템이 원하는 정답을 제공해야 함을 의미합니다. 간단하게하기 위해 시스템이 시작 프로그램 상태를 최종 상태로 올바르게 매핑해야한다고 말할 수 있습니다.
안전 속성
안전 속성은 프로그램 또는 시스템이 “good” 또는 “safe” 상태 및 아무것도하지 않습니다 “bad”.
Liveness 속성
이 속성은 프로그램 또는 시스템이 “make progress” 그리고 그것은 어떤 바람직한 상태에 도달 할 것입니다.
동시 시스템의 행위자
이것은 여러 프로세스와 스레드가있을 수있는 동시 시스템의 공통 속성 중 하나이며, 동시에 실행되어 자체 작업을 진행합니다. 이러한 프로세스와 스레드를 동시 시스템의 행위자라고합니다.
동시 시스템의 리소스
행위자는 자신의 작업을 수행하기 위해 메모리, 디스크, 프린터 등과 같은 리소스를 활용해야합니다.
특정 규칙 세트
모든 동시 시스템에는 행위자가 수행 할 작업의 종류와 각각에 대한 타이밍을 정의하는 일련의 규칙이 있어야합니다. 작업은 잠금 획득, 메모리 공유, 상태 수정 등이 될 수 있습니다.
동시 시스템의 장벽
데이터 공유
동시 시스템을 구현하는 동안 중요한 문제는 여러 스레드 또는 프로세스 간의 데이터 공유입니다. 실제로 프로그래머는 공유 데이터에 대한 모든 액세스가 직렬화되고 한 번에 하나의 스레드 또는 프로세스 만 공유 데이터에 액세스 할 수 있도록 잠금이 공유 데이터를 보호하는지 확인해야합니다. 여러 스레드 또는 프로세스가 모두 동일한 공유 데이터에 액세스하려고 할 때 모두가 아니라 적어도 하나는 차단되고 유휴 상태로 유지됩니다. 즉, 잠금이 적용되면 한 번에 하나의 프로세스 또는 스레드 만 사용할 수 있다고 말할 수 있습니다. 위에서 언급 한 장벽을 제거하는 몇 가지 간단한 해결책이있을 수 있습니다.
데이터 공유 제한
가장 간단한 해결책은 변경 가능한 데이터를 공유하지 않는 것입니다. 이 경우 명시 적 잠금을 사용할 필요가 없으며 상호 데이터로 인한 동시성 장벽이 해결됩니다.
데이터 구조 지원
여러 번 동시 프로세스가 동일한 데이터에 동시에 액세스해야합니다. 명시 적 잠금을 사용하는 것보다 다른 솔루션은 동시 액세스를 지원하는 데이터 구조를 사용하는 것입니다. 예를 들어, 우리는queue스레드로부터 안전한 큐를 제공하는 모듈. 우리는 또한 사용할 수 있습니다multiprocessing.JoinableQueue 다중 처리 기반 동시성을위한 클래스.
불변 데이터 전송
때로는 사용중인 데이터 구조 (예 : 동시성 큐)가 적합하지 않은 경우 잠금없이 변경할 수없는 데이터를 전달할 수 있습니다.
가변 데이터 전송
위의 솔루션에서 불변 데이터가 아닌 변경 가능한 데이터 만 전달해야하는 경우 읽기 전용 인 가변 데이터를 전달할 수 있다고 가정합니다.
I / O 리소스 공유
동시 시스템 구현의 또 다른 중요한 문제는 스레드 또는 프로세스에 의한 I / O 리소스 사용입니다. 한 스레드 또는 프로세스가 I / O를 오랫동안 사용하고 있고 다른 스레드가 유휴 상태 일 때 문제가 발생합니다. I / O가 많은 애플리케이션에서 작업하는 동안 이러한 종류의 장벽을 볼 수 있습니다. 웹 브라우저에서 페이지를 요청하는 예제의 도움으로 이해할 수 있습니다. 무거운 응용 프로그램입니다. 여기에서 데이터 요청 속도가 데이터 소비 속도보다 느리면 동시 시스템에 I / O 장벽이 있습니다.
다음 Python 스크립트는 웹 페이지를 요청하고 네트워크가 요청 된 페이지를 가져 오는 데 걸린 시간을 가져 오는 것입니다.
import urllib.request
import time
ts = time.time()
req = urllib.request.urlopen('http://www.tutorialspoint.com')
pageHtml = req.read()
te = time.time()
print("Page Fetching Time : {} Seconds".format (te-ts))
위 스크립트를 실행하면 아래와 같이 페이지 가져 오기 시간을 얻을 수 있습니다.
산출
Page Fetching Time: 1.0991398811340332 Seconds
페이지를 가져 오는 데 걸리는 시간이 1 초 이상임을 알 수 있습니다. 이제 수천 개의 서로 다른 웹 페이지를 가져 오려면 네트워크에 걸리는 시간을 이해할 수 있습니다.
병렬 처리 란 무엇입니까?
병렬성은 작업을 동시에 처리 할 수있는 하위 작업으로 분할하는 기술로 정의 할 수 있습니다. 위에서 논의한 것처럼 동시에 두 개 이상의 이벤트가 발생하는 동시성과 반대입니다. 우리는 그것을 도식적으로 이해할 수 있습니다. 작업은 다음과 같이 병렬로 처리 할 수있는 여러 하위 작업으로 나뉩니다.
동시성과 병렬성의 차이에 대한 더 많은 아이디어를 얻으려면 다음 사항을 고려하십시오.
동시이지만 병렬이 아님
응용 프로그램은 동시적일 수 있지만 병렬이 아님은 동시에 둘 이상의 작업을 처리하지만 작업이 하위 작업으로 분할되지 않음을 의미합니다.
병렬이지만 동시에는 아님
응용 프로그램은 병렬 일 수 있지만 동시 작업은 아님을 의미합니다. 즉, 한 번에 하나의 작업에서만 작동하고 하위 작업으로 분할 된 작업을 병렬로 처리 할 수 있습니다.
병렬도 동시도 아님
애플리케이션은 병렬 또는 동시가 될 수 없습니다. 즉, 한 번에 하나의 작업에서만 작동하며 작업이 하위 작업으로 분할되지 않습니다.
병렬 및 동시
응용 프로그램은 병렬 및 동시 작업이 될 수 있습니다. 즉, 둘 다 한 번에 여러 작업에서 작동하고 작업이 병렬로 실행되도록 하위 작업으로 나뉩니다.
병렬성의 필요성
단일 CPU의 서로 다른 코어 또는 네트워크 내에서 연결된 여러 컴퓨터간에 하위 작업을 분산하여 병렬화를 달성 할 수 있습니다.
병렬화를 달성해야하는 이유를 이해하려면 다음과 같은 중요한 사항을 고려하십시오.
효율적인 코드 실행
병렬 처리의 도움으로 코드를 효율적으로 실행할 수 있습니다. 부분적으로 동일한 코드가 병렬로 실행되기 때문에 시간을 절약 할 수 있습니다.
순차 컴퓨팅보다 빠름
순차 컴퓨팅은 더 빠른 컴퓨팅 결과를 얻을 수없는 물리적 및 실제적 요인에 의해 제한됩니다. 반면에이 문제는 병렬 컴퓨팅으로 해결되며 순차 컴퓨팅보다 빠른 컴퓨팅 결과를 제공합니다.
실행 시간 단축
병렬 처리는 프로그램 코드의 실행 시간을 줄여줍니다.
병렬 처리의 실제 사례에 대해 이야기하면, 컴퓨터의 그래픽 카드는 독립적으로 작동하고 동시에 실행을 수행 할 수있는 수백 개의 개별 처리 코어를 가지고 있기 때문에 병렬 처리의 진정한 힘을 강조하는 예입니다. 이러한 이유로 우리는 고급 애플리케이션과 게임도 실행할 수 있습니다.
구현을위한 프로세서 이해
동시성, 병렬성 및 이들 간의 차이점에 대해 알고 있지만 구현 될 시스템은 어떻습니까? 소프트웨어를 설계하는 동안 정보에 입각 한 결정을 내릴 수있는 이점을 제공하기 때문에 우리가 구현할 시스템에 대한 이해가 매우 필요합니다. 다음 두 종류의 프로세서가 있습니다.
단일 코어 프로세서
단일 코어 프로세서는 주어진 시간에 하나의 스레드를 실행할 수 있습니다. 이러한 프로세서는context switching특정 시간에 스레드에 필요한 모든 정보를 저장 한 다음 나중에 정보를 복원합니다. 컨텍스트 전환 메커니즘은 주어진 시간 내에 여러 스레드에서 진행하는 데 도움이되며 시스템이 여러 작업을 수행하는 것처럼 보입니다.
단일 코어 프로세서에는 많은 이점이 있습니다. 이러한 프로세서는 전력을 덜 필요로하며 여러 코어 사이에 복잡한 통신 프로토콜이 없습니다. 반면에 단일 코어 프로세서의 속도는 제한적이며 더 큰 응용 프로그램에는 적합하지 않습니다.
멀티 코어 프로세서
멀티 코어 프로세서에는 여러 개의 독립적 인 처리 장치가 있습니다. cores.
각 코어에는 저장된 명령 시퀀스를 실행하는 데 필요한 모든 것이 포함되어 있으므로 이러한 프로세서에는 컨텍스트 전환 메커니즘이 필요하지 않습니다.
가져 오기-디코딩-실행주기
멀티 코어 프로세서의 코어는 실행주기를 따릅니다. 이주기를Fetch-Decode-Execute주기. 그것은 다음 단계를 포함합니다-
술책
이것은 프로그램 메모리에서 명령을 가져 오는주기의 첫 번째 단계입니다.
풀다
최근에 가져온 명령어는 CPU의 다른 부분을 트리거하는 일련의 신호로 변환됩니다.
실행
페치 및 디코딩 된 명령어가 실행되는 마지막 단계입니다. 실행 결과는 CPU 레지스터에 저장됩니다.
여기에서 한 가지 장점은 멀티 코어 프로세서의 실행이 단일 코어 프로세서보다 빠르다는 것입니다. 더 큰 용도에 적합합니다. 반면에 여러 코어 간의 복잡한 통신 프로토콜이 문제입니다. 다중 코어는 단일 코어 프로세서보다 더 많은 전력을 필요로합니다.
프로그램 또는 동시 시스템을 설계 할 때 고려해야 할 다양한 시스템 및 메모리 아키텍처 스타일이 있습니다. 하나의 시스템 및 메모리 스타일이 하나의 작업에 적합 할 수 있지만 다른 작업에는 오류가 발생할 수 있기 때문에 매우 필요합니다.
동시성을 지원하는 컴퓨터 시스템 아키텍처
1972 년 Michael Flynn은 다양한 스타일의 컴퓨터 시스템 아키텍처를 분류하는 분류 체계를 제공했습니다. 이 분류는 다음과 같이 네 가지 스타일을 정의합니다.
- 단일 명령 스트림, 단일 데이터 스트림 (SISD)
- 단일 명령 스트림, 다중 데이터 스트림 (SIMD)
- 다중 명령 스트림, 단일 데이터 스트림 (MISD)
- 다중 명령 스트림, 다중 데이터 스트림 (MIMD).
단일 명령 스트림, 단일 데이터 스트림 (SISD)
이름에서 알 수 있듯이 이러한 종류의 시스템은 하나의 순차적 수신 데이터 스트림과 하나의 단일 처리 장치를 사용하여 데이터 스트림을 실행합니다. 그들은 병렬 컴퓨팅 아키텍처를 갖는 단일 프로세서 시스템과 같습니다. 다음은 SISD의 아키텍처입니다-
SISD의 장점
SISD 아키텍처의 장점은 다음과 같습니다.
- 적은 전력이 필요합니다.
- 여러 코어간에 복잡한 통신 프로토콜 문제가 없습니다.
SISD의 단점
SISD 아키텍처의 단점은 다음과 같습니다.
- SISD 아키텍처의 속도는 단일 코어 프로세서와 마찬가지로 제한됩니다.
- 더 큰 용도에는 적합하지 않습니다.
단일 명령 스트림, 다중 데이터 스트림 (SIMD)
이름에서 알 수 있듯이 이러한 종류의 시스템에는 여러 수신 데이터 스트림과 주어진 시간에 단일 명령에 대해 작동 할 수있는 처리 장치 수가 있습니다. 병렬 컴퓨팅 아키텍처를 가진 다중 프로세서 시스템과 같습니다. 다음은 SIMD의 아키텍처입니다-
SIMD의 가장 좋은 예는 그래픽 카드입니다. 이 카드에는 수백 개의 개별 처리 장치가 있습니다. SISD와 SIMD의 계산 차이에 대해 이야기하면 배열 추가[5, 15, 20] 과 [15, 25, 10],SISD 아키텍처는 세 가지 다른 추가 작업을 수행해야합니다. 반면에 SIMD 아키텍처를 사용하면 단일 추가 작업으로 추가 할 수 있습니다.
SIMD의 장점
SIMD 아키텍처의 장점은 다음과 같습니다.
여러 요소에 대해 동일한 작업을 하나의 명령으로 만 수행 할 수 있습니다.
프로세서의 코어 수를 늘려 시스템 처리량을 늘릴 수 있습니다.
처리 속도는 SISD 아키텍처보다 빠릅니다.
SIMD의 단점
SIMD 아키텍처의 단점은 다음과 같습니다.
- 프로세서 코어 수 사이에는 복잡한 통신이 있습니다.
- 비용은 SISD 아키텍처보다 높습니다.
MISD (Multiple Instruction Single Data) 스트림
MISD 스트림이있는 시스템에는 동일한 데이터 세트에 대해 서로 다른 명령을 실행하여 서로 다른 작업을 수행하는 여러 처리 장치가 있습니다. 다음은 MISD의 아키텍처입니다-
MISD 아키텍처의 대표자는 아직 상업적으로 존재하지 않습니다.
MIMD (Multiple Instruction Multiple Data) 스트림
MIMD 아키텍처를 사용하는 시스템에서 다중 프로세서 시스템의 각 프로세서는 병렬로 서로 다른 데이터 세트 세트에서 독립적으로 서로 다른 명령 세트를 실행할 수 있습니다. 단일 작업이 여러 데이터 세트에서 실행되는 SIMD 아키텍처와 반대입니다. 다음은 MIMD의 아키텍처입니다-
일반 멀티 프로세서는 MIMD 아키텍처를 사용합니다. 이러한 아키텍처는 기본적으로 컴퓨터 지원 설계 / 컴퓨터 지원 제조, 시뮬레이션, 모델링, 통신 스위치 등과 같은 여러 응용 분야에서 사용됩니다.
동시성을 지원하는 메모리 아키텍처
동시성 및 병렬 처리와 같은 개념으로 작업하는 동안 항상 프로그램 속도를 높일 필요가 있습니다. 컴퓨터 설계자가 찾은 한 가지 해결책은 공유 메모리 다중 컴퓨터를 만드는 것입니다. 즉, 단일 물리적 주소 공간을 가진 컴퓨터는 프로세서가 가지고있는 모든 코어에서 액세스합니다. 이 시나리오에서는 여러 가지 아키텍처 스타일이있을 수 있지만 다음은 세 가지 중요한 아키텍처 스타일입니다.
UMA (Uniform Memory Access)
이 모델에서 모든 프로세서는 물리적 메모리를 균일하게 공유합니다. 모든 프로세서는 모든 메모리 단어에 대해 동일한 액세스 시간을 갖습니다. 각 프로세서에는 개인 캐시 메모리가있을 수 있습니다. 주변 장치는 일련의 규칙을 따릅니다.
모든 프로세서가 모든 주변 장치에 동등하게 액세스 할 수있는 경우 시스템을 symmetric multiprocessor. 하나 또는 몇 개의 프로세서 만 주변 장치에 액세스 할 수있는 경우 시스템을asymmetric multiprocessor.
NUMA (Non-Uniform Memory Access)
NUMA 멀티 프로세서 모델에서 액세스 시간은 메모리 워드의 위치에 따라 다릅니다. 여기서 공유 메모리는 로컬 메모리라고하는 모든 프로세서에 물리적으로 분산됩니다. 모든 로컬 메모리 모음은 모든 프로세서에서 액세스 할 수있는 전역 주소 공간을 형성합니다.
캐시 전용 메모리 아키텍처 (COMA)
COMA 모델은 NUMA 모델의 특수 버전입니다. 여기에서 모든 분산 된 메인 메모리는 캐시 메모리로 변환됩니다.
일반적으로 실은 일반적으로면이나 실크 직물로 된 매우 가늘고 꼬인 끈이며 옷을 재봉하는 데 사용됩니다. 같은 용어 스레드는 컴퓨터 프로그래밍 세계에서도 사용됩니다. 자, 옷 재봉에 사용되는 실과 컴퓨터 프로그래밍에 사용되는 실을 어떻게 연관시킬까요? 두 스레드가 수행하는 역할은 여기서 비슷합니다. 옷에서 실은 천을 함께 잡고 다른쪽에는 컴퓨터 프로그래밍에서 실이 컴퓨터 프로그램을 잡고 프로그램이 한 번에 여러 작업을 연속적으로 실행할 수 있도록합니다.
Thread운영 체제에서 가장 작은 실행 단위입니다. 그 자체로는 프로그램이 아니라 프로그램 내에서 실행됩니다. 즉, 스레드는 서로 독립적이지 않으며 다른 스레드와 코드 섹션, 데이터 섹션 등을 공유합니다. 이러한 스레드를 경량 프로세스라고도합니다.
스레드 상태
스레드의 기능을 심층적으로 이해하려면 스레드의 수명주기 또는 다른 스레드 상태에 대해 배워야합니다. 일반적으로 스레드는 다섯 가지 상태로 존재할 수 있습니다. 다른 상태는 다음과 같습니다.
새 스레드
새 스레드는 새 상태에서 수명주기를 시작합니다. 그러나이 단계에서는 아직 시작되지 않았으며 리소스가 할당되지 않았습니다. 그것은 단지 객체의 인스턴스라고 말할 수 있습니다.
실행 가능
새로 태어난 스레드가 시작되면 스레드가 실행 가능해집니다. 즉 실행 대기 중입니다. 이 상태에서는 모든 리소스가 있지만 여전히 작업 스케줄러가 실행되도록 예약하지 않았습니다.
달리는
이 상태에서 스레드는 작업을 진행하고 실행할 작업 스케줄러가 선택한 작업을 실행합니다. 이제 스레드는 dead 상태 또는 non-runnable / wait 상태가 될 수 있습니다.
비 실행 / 대기
이 상태에서 스레드는 일부 I / O 요청의 응답을 기다리거나 다른 스레드의 실행 완료를 기다리고 있기 때문에 일시 중지됩니다.
죽은
실행 가능한 스레드는 작업을 완료하거나 그렇지 않으면 종료 될 때 종료 됨 상태가됩니다.
다음 다이어그램은 스레드의 전체 수명주기를 보여줍니다.
실의 종류
이 섹션에서는 다양한 유형의 스레드를 살펴 보겠습니다. 유형은 아래에 설명되어 있습니다-
사용자 수준 스레드
사용자 관리 스레드입니다.
이 경우 스레드 관리 커널은 스레드의 존재를 인식하지 못합니다. 스레드 라이브러리에는 스레드 생성 및 삭제, 스레드 간 메시지 및 데이터 전달, 스레드 실행 예약 및 스레드 컨텍스트 저장 및 복원을위한 코드가 포함되어 있습니다. 응용 프로그램은 단일 스레드로 시작됩니다.
사용자 수준 스레드의 예는 다음과 같습니다.
- 자바 스레드
- POSIX 스레드
사용자 수준 스레드의 장점
다음은 사용자 수준 스레드의 다양한 장점입니다.
- 스레드 전환에는 커널 모드 권한이 필요하지 않습니다.
- 사용자 수준 스레드는 모든 운영 체제에서 실행할 수 있습니다.
- 일정은 사용자 수준 스레드에서 응용 프로그램에 따라 다를 수 있습니다.
- 사용자 수준 스레드는 생성 및 관리가 빠릅니다.
사용자 수준 스레드의 단점
다음은 사용자 수준 스레드의 다른 단점입니다.
- 일반적인 운영 체제에서 대부분의 시스템 호출은 차단됩니다.
- 다중 스레드 응용 프로그램은 다중 처리를 활용할 수 없습니다.
커널 수준 스레드
운영 체제 관리 스레드는 운영 체제 코어 인 커널에서 작동합니다.
이 경우 커널은 스레드 관리를 수행합니다. 응용 프로그램 영역에는 스레드 관리 코드가 없습니다. 커널 스레드는 운영 체제에서 직접 지원됩니다. 모든 응용 프로그램을 다중 스레드로 프로그래밍 할 수 있습니다. 응용 프로그램 내의 모든 스레드는 단일 프로세스 내에서 지원됩니다.
커널은 프로세스 전체와 프로세스 내의 개별 스레드에 대한 컨텍스트 정보를 유지합니다. 커널에 의한 스케줄링은 스레드 기반으로 수행됩니다. Kernel은 Kernel 공간에서 스레드 생성, 스케줄링 및 관리를 수행합니다. 커널 스레드는 일반적으로 사용자 스레드보다 생성 및 관리 속도가 느립니다. 커널 수준 스레드의 예는 Windows, Solaris입니다.
커널 수준 스레드의 장점
다음은 커널 레벨 스레드의 다른 장점입니다.
커널은 여러 프로세스에서 동일한 프로세스의 여러 스레드를 동시에 예약 할 수 있습니다.
프로세스의 한 스레드가 차단되면 커널은 동일한 프로세스의 다른 스레드를 예약 할 수 있습니다.
커널 루틴 자체는 다중 스레드가 될 수 있습니다.
커널 수준 스레드의 단점
커널 스레드는 일반적으로 사용자 스레드보다 생성 및 관리 속도가 느립니다.
동일한 프로세스 내에서 한 스레드에서 다른 스레드로 제어를 전송하려면 모드를 커널로 전환해야합니다.
스레드 제어 블록-TCB
스레드 제어 블록 (TCB)은 주로 스레드에 대한 정보를 포함하는 운영 체제 커널의 데이터 구조로 정의 할 수 있습니다. TCB에 저장된 스레드 별 정보는 각 프로세스에 대한 몇 가지 중요한 정보를 강조합니다.
TCB에 포함 된 스레드와 관련된 다음 사항을 고려하십시오.
Thread identification − 모든 새 스레드에 할당 된 고유 한 스레드 ID (tid)입니다.
Thread state − 스레드의 상태 (Running, Runnable, Non-Running, Dead)와 관련된 정보를 포함합니다.
Program Counter (PC) − 스레드의 현재 프로그램 명령을 가리 킵니다.
Register set − 계산을 위해 할당 된 스레드의 레지스터 값을 포함합니다.
Stack Pointer− 프로세스에서 스레드의 스택을 가리 킵니다. 스레드 범위 아래의 지역 변수를 포함합니다.
Pointer to PCB − 해당 스레드를 생성 한 프로세스에 대한 포인터를 포함합니다.
프로세스와 스레드의 관계
멀티 스레딩에서 프로세스와 스레드는 컴퓨터가 한 번에 하나 이상의 작업을 수행 할 수 있도록하는 동일한 목표를 가진 매우 밀접하게 관련된 두 용어입니다. 프로세스는 하나 이상의 스레드를 포함 할 수 있지만 반대로 스레드는 프로세스를 포함 할 수 없습니다. 그러나 둘 다 두 가지 기본 실행 단위로 남아 있습니다. 일련의 명령을 실행하는 프로그램은 프로세스와 스레드를 모두 시작합니다.
다음 표는 프로세스와 스레드 간의 비교를 보여줍니다-
방법 | 실 |
---|---|
프로세스가 무겁거나 리소스 집약적입니다. | 스레드는 프로세스보다 적은 리소스를 사용하는 경량입니다. |
프로세스 전환에는 운영 체제와의 상호 작용이 필요합니다. | 스레드 전환은 운영 체제와 상호 작용할 필요가 없습니다. |
여러 처리 환경에서 각 프로세스는 동일한 코드를 실행하지만 자체 메모리 및 파일 리소스가 있습니다. | 모든 스레드는 동일한 열린 파일 세트, 하위 프로세스를 공유 할 수 있습니다. |
한 프로세스가 차단되면 첫 번째 프로세스가 차단 해제 될 때까지 다른 프로세스를 실행할 수 없습니다. | 하나의 스레드가 차단되고 대기하는 동안 동일한 작업의 두 번째 스레드가 실행될 수 있습니다. |
스레드를 사용하지 않는 여러 프로세스는 더 많은 리소스를 사용합니다. | 다중 스레드 프로세스는 더 적은 리소스를 사용합니다. |
여러 프로세스에서 각 프로세스는 다른 프로세스와 독립적으로 작동합니다. | 한 스레드는 다른 스레드의 데이터를 읽거나 쓰거나 변경할 수 있습니다. |
상위 프로세스에 변경 사항이 있으면 하위 프로세스에 영향을주지 않습니다. | 메인 스레드에 변경 사항이 있으면 해당 프로세스의 다른 스레드 동작에 영향을 미칠 수 있습니다. |
형제 프로세스와 통신하려면 프로세스가 프로세스 간 통신을 사용해야합니다. | 스레드는 해당 프로세스의 다른 스레드와 직접 통신 할 수 있습니다. |
멀티 스레딩의 개념
앞서 논의했듯이 멀티 스레딩은 CPU가 여러 스레드를 동시에 실행하여 운영 체제 사용을 관리하는 기능입니다. 멀티 스레딩의 주요 개념은 프로세스를 여러 스레드로 분할하여 병렬화를 달성하는 것입니다. 보다 간단한 방법으로 멀티 스레딩은 스레드 개념을 사용하여 멀티 태스킹을 달성하는 방법이라고 말할 수 있습니다.
멀티 스레딩의 개념은 다음 예제를 통해 이해할 수 있습니다.
예
프로세스를 실행하고 있다고 가정합니다. 프로세스는 무언가를 쓰기 위해 MS 워드를 여는 것일 수 있습니다. 이러한 과정에서 하나의 스레드는 MS 워드를 열기 위해 할당되고 다른 스레드는 쓰기를 위해 필요합니다. 이제 우리가 무언가를 편집하고 싶다면 편집 작업을 수행하기 위해 다른 스레드가 필요하다고 가정합니다.
다음 다이어그램은 메모리에 여러 스레드가 존재하는 방식을 이해하는 데 도움이됩니다.
위의 다이어그램에서 모든 스레드가 자체 레지스터 세트와 로컬 변수를 포함하는 하나의 프로세스 내에 둘 이상의 스레드가 존재할 수 있음을 알 수 있습니다. 그 외에는 프로세스의 모든 스레드가 전역 변수를 공유합니다.
멀티 스레딩의 장점
이제 멀티 스레딩의 몇 가지 장점을 살펴 보겠습니다. 장점은 다음과 같습니다-
Speed of communication − 멀티 스레딩은 각 코어 또는 프로세서가 개별 스레드를 동시에 처리하므로 계산 속도를 향상시킵니다.
Program remains responsive − 한 스레드가 입력을 대기하고 다른 스레드가 동시에 GUI를 실행하기 때문에 프로그램이 응답 성을 유지할 수 있습니다.
Access to global variables − 멀티 스레딩에서 특정 프로세스의 모든 스레드는 전역 변수에 액세스 할 수 있으며 전역 변수에 변경 사항이 있으면 다른 스레드에서도 볼 수 있습니다.
Utilization of resources − 각 프로그램에서 여러 스레드를 실행하면 CPU를 더 잘 사용할 수 있고 CPU의 유휴 시간이 줄어 듭니다.
Sharing of data − 프로그램 내의 스레드가 동일한 데이터를 공유 할 수 있으므로 각 스레드에 대한 추가 공간이 필요하지 않습니다.
멀티 스레딩의 단점
이제 멀티 스레딩의 몇 가지 단점을 살펴 보겠습니다. 단점은 다음과 같습니다-
Not suitable for single processor system − 멀티 스레딩은 멀티 프로세서 시스템의 성능에 비해 단일 프로세서 시스템의 계산 속도 측면에서 성능을 달성하기 어렵다는 것을 발견했습니다.
Issue of security − 프로그램 내의 모든 스레드가 동일한 데이터를 공유한다는 것을 알고 있으므로 알 수없는 스레드가 데이터를 변경할 수 있으므로 항상 보안 문제가 있습니다.
Increase in complexity − 멀티 스레딩은 프로그램의 복잡성을 증가시키고 디버깅이 어려워집니다.
Lead to deadlock state − 멀티 스레딩은 프로그램이 교착 상태에 도달 할 수있는 잠재적 위험을 초래할 수 있습니다.
Synchronization required− 상호 배제를 피하기 위해 동기화가 필요합니다. 이것은 더 많은 메모리와 CPU 사용률로 이어집니다.
이 장에서는 파이썬에서 스레드를 구현하는 방법을 배웁니다.
스레드 구현을위한 Python 모듈
Python 스레드는 스레드가 프로세스보다 훨씬 적은 메모리를 차지하기 때문에 경량 프로세스라고도합니다. 스레드를 사용하면 한 번에 여러 작업을 수행 할 수 있습니다. Python에는 프로그램에서 스레드를 구현하는 다음 두 모듈이 있습니다.
<_thread>module
<threading>module
이 두 모듈의 주요 차이점은 <_thread> 모듈은 스레드를 함수로 취급하는 반면, <threading>모듈은 모든 스레드를 객체로 취급하고 객체 지향 방식으로 구현합니다. 또한<_thread>모듈은 낮은 수준의 스레딩에 효과적이며 <threading> 기준 치수.
<_thread> 모듈
이전 버전의 Python에서는 <thread>모듈이지만 꽤 오랫동안 "사용되지 않는"것으로 간주되었습니다. 사용자는<threading>대신 모듈. 따라서 Python 3에서는 "thread"모듈을 더 이상 사용할 수 없습니다. 이름이 "로 변경되었습니다.<_thread>"는 Python3의 이전 버전 비 호환성입니다.
의 도움으로 새 스레드를 생성하려면 <_thread> 모듈을 호출해야합니다. start_new_thread그것의 방법. 이 방법의 작동은 다음 구문의 도움으로 이해할 수 있습니다.
_thread.start_new_thread ( function, args[, kwargs] )
여기-
args 인수의 튜플입니다.
kwargs 키워드 인수의 선택적 사전입니다.
인수를 전달하지 않고 함수를 호출하려면 빈 인수 튜플을 사용해야합니다. args.
이 메서드 호출은 즉시 반환되고 자식 스레드가 시작되며 전달 된 인수 목록 (있는 경우)과 함께 함수를 호출합니다. 스레드는 함수가 반환 될 때 종료됩니다.
예
다음은 다음을 사용하여 새 스레드를 생성하는 예입니다. <_thread>기준 치수. 여기서는 start_new_thread () 메서드를 사용하고 있습니다.
import _thread
import time
def print_time( threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print ("%s: %s" % ( threadName, time.ctime(time.time()) ))
try:
_thread.start_new_thread( print_time, ("Thread-1", 2, ) )
_thread.start_new_thread( print_time, ("Thread-2", 4, ) )
except:
print ("Error: unable to start thread")
while 1:
pass
산출
다음 출력은 새 스레드의 생성을 이해하는 데 도움이됩니다. <_thread> 기준 치수.
Thread-1: Mon Apr 23 10:03:33 2018
Thread-2: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:37 2018
Thread-2: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:41 2018
Thread-2: Mon Apr 23 10:03:43 2018
Thread-2: Mon Apr 23 10:03:47 2018
Thread-2: Mon Apr 23 10:03:51 2018
<threading> 모듈
그만큼 <threading>모듈은 객체 지향 방식으로 구현하고 모든 스레드를 객체로 취급합니다. 따라서 <_thread> 모듈보다 훨씬 더 강력하고 높은 수준의 스레드 지원을 제공합니다. 이 모듈은 Python 2.4에 포함되어 있습니다.
<threading> 모듈의 추가 메서드
그만큼 <threading> 모듈은 모든 방법을 포함합니다 <_thread>모듈이지만 추가 메소드도 제공합니다. 추가 방법은 다음과 같습니다.
threading.activeCount() −이 메서드는 활성 상태 인 스레드 개체의 수를 반환합니다.
threading.currentThread() −이 메서드는 호출자의 스레드 제어에있는 스레드 개체의 수를 반환합니다.
threading.enumerate() −이 메서드는 현재 활성화 된 모든 스레드 개체의 목록을 반환합니다.
run() − run () 메서드는 스레드의 진입 점입니다.
start() − start () 메서드는 run 메서드를 호출하여 스레드를 시작합니다.
join([time]) − join ()은 스레드가 종료되기를 기다립니다.
isAlive() − isAlive () 메서드는 스레드가 아직 실행 중인지 확인합니다.
getName() − getName () 메서드는 스레드의 이름을 반환합니다.
setName() − setName () 메서드는 스레드의 이름을 설정합니다.
스레딩을 구현하려면 <threading> 모듈에는 Thread 다음 메소드를 제공하는 클래스-
<threading> 모듈을 사용하여 스레드를 만드는 방법은 무엇입니까?
이 섹션에서는 다음을 사용하여 스레드를 만드는 방법을 배웁니다. <threading>기준 치수. 다음 단계를 따라 <threading> 모듈을 사용하여 새 스레드를 만듭니다.
Step 1 −이 단계에서는 새로운 하위 클래스를 정의해야합니다. Thread 수업.
Step 2 − 그런 다음 추가 인수를 추가하려면 __init__(self [,args]) 방법.
Step 3 −이 단계에서는 스레드가 시작될 때 수행해야하는 작업을 구현하기 위해 run (self [, args]) 메서드를 재정의해야합니다.
이제 새로 만든 후 Thread 하위 클래스의 경우 인스턴스를 만든 다음 새 스레드를 시작할 수 있습니다. start(), 차례로 run() 방법.
예
이 예제를 사용하여 새 스레드를 생성하는 방법을 알아보십시오. <threading> 기준 치수.
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print ("Starting " + self.name)
print_time(self.name, self.counter, 5)
print ("Exiting " + self.name)
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("Exiting Main Thread")
Starting Thread-1
Starting Thread-2
산출
이제 다음 출력을 고려하십시오.
Thread-1: Mon Apr 23 10:52:09 2018
Thread-1: Mon Apr 23 10:52:10 2018
Thread-2: Mon Apr 23 10:52:10 2018
Thread-1: Mon Apr 23 10:52:11 2018
Thread-1: Mon Apr 23 10:52:12 2018
Thread-2: Mon Apr 23 10:52:12 2018
Thread-1: Mon Apr 23 10:52:13 2018
Exiting Thread-1
Thread-2: Mon Apr 23 10:52:14 2018
Thread-2: Mon Apr 23 10:52:16 2018
Thread-2: Mon Apr 23 10:52:18 2018
Exiting Thread-2
Exiting Main Thread
다양한 스레드 상태를위한 Python 프로그램
스레드 상태에는 새, 실행 가능, 실행 중, 대기 중 및 종료의 다섯 가지가 있습니다. 이 5 개 중 5 개 중 주로 달리기, 대기 중, 사망의 세 가지 상태에 중점을 둡니다. 스레드는 리소스를 실행 중 상태로 가져와 대기 상태의 리소스를 기다립니다. 리소스의 최종 릴리스 (실행되고 획득 된 경우)가 데드 상태에 있습니다.
start (), sleep () 및 join () 메서드를 사용하는 다음 Python 프로그램은 스레드가 각각 실행 중, 대기 중 및 사용 불능 상태로 진입 한 방법을 보여줍니다.
Step 1 − 필요한 모듈, <threading> 및 <time> 가져 오기
import threading
import time
Step 2 − 스레드를 생성하는 동안 호출 될 함수를 정의합니다.
def thread_states():
print("Thread entered in running state")
Step 3 − time 모듈의 sleep () 메서드를 사용하여 스레드가 2 초 동안 대기하도록합니다.
time.sleep(2)
Step 4 − 이제 위에서 정의한 함수의 인수를받는 T1이라는 스레드를 생성합니다.
T1 = threading.Thread(target=thread_states)
Step 5− 이제 start () 함수의 도움으로 스레드를 시작할 수 있습니다. 기능을 정의하는 동안 우리가 설정 한 메시지를 생성합니다.
T1.start()
Thread entered in running state
Step 6 − 이제 드디어 실행을 마친 후 join () 메소드로 스레드를 종료 할 수 있습니다.
T1.join()
Python에서 스레드 시작
파이썬에서는 여러 가지 방법으로 새 스레드를 시작할 수 있지만 그중 가장 쉬운 방법은 단일 함수로 정의하는 것입니다. 함수를 정의한 후이를 새로운 대상으로 전달할 수 있습니다.threading.Thread개체 등. 함수가 어떻게 작동하는지 이해하려면 다음 Python 코드를 실행하십시오.
import threading
import time
import random
def Thread_execution(i):
print("Execution of Thread {} started\n".format(i))
sleepTime = random.randint(1,4)
time.sleep(sleepTime)
print("Execution of Thread {} finished".format(i))
for i in range(4):
thread = threading.Thread(target=Thread_execution, args=(i,))
thread.start()
print("Active Threads:" , threading.enumerate())
산출
Execution of Thread 0 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>]
Execution of Thread 1 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>,
<Thread(Thread-3577, started 3080)>]
Execution of Thread 2 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>,
<Thread(Thread-3577, started 3080)>,
<Thread(Thread-3578, started 2268)>]
Execution of Thread 3 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>,
<Thread(Thread-3577, started 3080)>,
<Thread(Thread-3578, started 2268)>,
<Thread(Thread-3579, started 4520)>]
Execution of Thread 0 finished
Execution of Thread 1 finished
Execution of Thread 2 finished
Execution of Thread 3 finished
Python의 데몬 스레드
파이썬에서 데몬 스레드를 구현하기 전에 데몬 스레드와 그 사용법에 대해 알아야합니다. 컴퓨팅 측면에서 데몬은 데이터 전송, 파일 전송 등과 같은 다양한 서비스에 대한 요청을 처리하는 백그라운드 프로세스입니다. 더 이상 필요하지 않으면 휴면 상태가됩니다. 데몬이 아닌 스레드의 도움으로 동일한 작업을 수행 할 수도 있습니다. 그러나이 경우 주 스레드는 데몬이 아닌 스레드를 수동으로 추적해야합니다. 반면에 데몬 스레드를 사용하는 경우 메인 스레드는이를 완전히 잊을 수 있으며 메인 스레드가 종료되면 종료됩니다. 데몬 스레드에 대한 또 다른 중요한 점은 완료되지 않거나 중간에 죽어도 영향을주지 않는 비 필수 작업에만 사용하도록 선택할 수 있다는 것입니다. 다음은 파이썬에서 데몬 스레드의 구현입니다-
import threading
import time
def nondaemonThread():
print("starting my thread")
time.sleep(8)
print("ending my thread")
def daemonThread():
while True:
print("Hello")
time.sleep(2)
if __name__ == '__main__':
nondaemonThread = threading.Thread(target = nondaemonThread)
daemonThread = threading.Thread(target = daemonThread)
daemonThread.setDaemon(True)
daemonThread.start()
nondaemonThread.start()
위의 코드에는 두 가지 기능이 있습니다. >nondaemonThread() 과 >daemonThread(). 첫 번째 함수는 상태를 인쇄하고 8 초 후에 휴면하는 반면 deamonThread () 함수는 2 초마다 무기한으로 Hello를 인쇄합니다. 다음 출력을 통해 nondaemon과 daemon 스레드의 차이점을 이해할 수 있습니다.
Hello
starting my thread
Hello
Hello
Hello
Hello
ending my thread
Hello
Hello
Hello
Hello
Hello
스레드 동기화는 두 개 이상의 동시 스레드가 중요 섹션으로 알려진 프로그램 세그먼트에 동시에 액세스하지 않는다는 것을 확신 할 수있는 방법으로 정의 할 수 있습니다. 반면에 중요한 섹션은 공유 리소스에 액세스하는 프로그램의 일부입니다. 따라서 동기화는 리소스에 동시에 액세스하여 둘 이상의 스레드가 서로 인터페이스하지 않도록하는 프로세스라고 말할 수 있습니다. 아래 다이어그램은 4 개의 스레드가 프로그램의 중요 섹션에 동시에 액세스하려고 시도하는 것을 보여줍니다.
더 명확하게하기 위해 두 개 이상의 스레드가 동시에 목록에 개체를 추가하려고한다고 가정합니다. 이 행위는 하나 또는 모든 객체를 삭제하거나 목록의 상태를 완전히 손상 시키므로 성공적인 종료로 이어질 수 없습니다. 여기서 동기화의 역할은 한 번에 하나의 스레드 만 목록에 액세스 할 수 있다는 것입니다.
스레드 동기화 문제
동시 프로그래밍을 구현하거나 동기화 기본 요소를 적용하는 동안 문제가 발생할 수 있습니다. 이 섹션에서는 두 가지 주요 문제에 대해 설명합니다. 문제는-
- Deadlock
- 경쟁 조건
경쟁 조건
이것은 동시 프로그래밍의 주요 문제 중 하나입니다. 공유 리소스에 대한 동시 액세스는 경쟁 조건으로 이어질 수 있습니다. 경쟁 조건은 두 개 이상의 스레드가 공유 데이터에 액세스 한 다음 그 값을 동시에 변경하려고 할 때 조건이 발생하는 것으로 정의 할 수 있습니다. 이로 인해 변수 값은 예측할 수없고 프로세스의 컨텍스트 전환 타이밍에 따라 달라질 수 있습니다.
예
경쟁 조건의 개념을 이해하려면이 예를 고려하십시오.
Step 1 −이 단계에서는 스레딩 모듈을 가져와야합니다 −
import threading
Step 2 − 이제 값을 0과 함께 x라고하는 전역 변수를 정의합니다 −
x = 0
Step 3 − 이제 우리는 increment_global() 이 전역 함수 x에서 1 씩 증가하는 함수
def increment_global():
global x
x += 1
Step 4 −이 단계에서는 taskofThread()함수, 지정된 횟수만큼 increment_global () 함수를 호출합니다. 이 예에서는 50000 배입니다.
def taskofThread():
for _ in range(50000):
increment_global()
Step 5− 이제 스레드 t1과 t2가 생성되는 main () 함수를 정의합니다. 둘 다 start () 함수의 도움으로 시작되고 join () 함수의 도움으로 작업이 끝날 때까지 기다립니다.
def main():
global x
x = 0
t1 = threading.Thread(target= taskofThread)
t2 = threading.Thread(target= taskofThread)
t1.start()
t2.start()
t1.join()
t2.join()
Step 6− 이제 main () 함수를 호출하려는 반복 횟수에 대한 범위를 제공해야합니다. 여기서는 5 번 호출합니다.
if __name__ == "__main__":
for i in range(5):
main()
print("x = {1} after Iteration {0}".format(i,x))
아래 표시된 출력에서 각 반복 후 x의 값이 100000으로 예상되므로 경쟁 조건의 효과를 볼 수 있습니다. 그러나 값에는 많은 변동이 있습니다. 이는 공유 전역 변수 x에 대한 스레드의 동시 액세스 때문입니다.
산출
x = 100000 after Iteration 0
x = 54034 after Iteration 1
x = 80230 after Iteration 2
x = 93602 after Iteration 3
x = 93289 after Iteration 4
잠금을 사용하여 경쟁 조건 처리
위 프로그램에서 경합 상태의 영향을 확인했듯이 여러 스레드 간의 경합 상태를 처리 할 수있는 동기화 도구가 필요합니다. Python에서<threading>모듈은 경쟁 조건을 처리하기 위해 Lock 클래스를 제공합니다. 또한Lock클래스는 여러 스레드 간의 경합 상태를 처리 할 수있는 다양한 메서드를 제공합니다. 방법은 아래에 설명되어 있습니다-
acquire () 메서드
이 방법은 잠금을 획득, 즉 차단하는 데 사용됩니다. 잠금은 다음 참 또는 거짓 값에 따라 차단 또는 차단되지 않을 수 있습니다.
With value set to True − acquire () 메서드가 기본 인수 인 True로 호출되면 잠금이 해제 될 때까지 스레드 실행이 차단됩니다.
With value set to False − acquire () 메서드가 기본 인수가 아닌 False로 호출되면 스레드 실행은 true로 설정 될 때까지 즉 잠길 때까지 차단되지 않습니다.
release () 메서드
이 방법은 잠금을 해제하는 데 사용됩니다. 다음은이 방법과 관련된 몇 가지 중요한 작업입니다.
자물쇠가 잠겨 있으면 release()방법은 그것을 잠금 해제합니다. 그 역할은 둘 이상의 스레드가 차단되고 잠금이 잠금 해제되기를 기다리는 경우 정확히 하나의 스레드가 진행되도록 허용하는 것입니다.
그것은 ThreadError 잠금이 이미 잠금 해제 된 경우.
이제 경쟁 조건을 피하기 위해 잠금 클래스와 메서드를 사용하여 위의 프로그램을 다시 작성할 수 있습니다. lock 인수를 사용하여 taskofThread () 메서드를 정의한 다음, 경쟁 조건을 피하기 위해 잠금 차단 및 비 차단을 위해 acquire () 및 release () 메서드를 사용해야합니다.
예
다음은 경쟁 조건을 처리하기위한 잠금 개념을 이해하기위한 파이썬 프로그램의 예입니다.
import threading
x = 0
def increment_global():
global x
x += 1
def taskofThread(lock):
for _ in range(50000):
lock.acquire()
increment_global()
lock.release()
def main():
global x
x = 0
lock = threading.Lock()
t1 = threading.Thread(target = taskofThread, args = (lock,))
t2 = threading.Thread(target = taskofThread, args = (lock,))
t1.start()
t2.start()
t1.join()
t2.join()
if __name__ == "__main__":
for i in range(5):
main()
print("x = {1} after Iteration {0}".format(i,x))
다음 출력은 경쟁 조건의 효과가 무시되었음을 보여줍니다. x의 값은 모든 반복 후 이제 100000이며, 이는이 프로그램의 기대에 따른 것입니다.
산출
x = 100000 after Iteration 0
x = 100000 after Iteration 1
x = 100000 after Iteration 2
x = 100000 after Iteration 3
x = 100000 after Iteration 4
Deadlocks-식사 철학자 문제
교착 상태는 동시 시스템을 설계하는 동안 직면 할 수있는 문제입니다. 다음과 같이 식사 철학자 문제의 도움으로이 문제를 설명 할 수 있습니다.
Edsger Dijkstra는 원래 교착 상태라는 동시 시스템의 가장 큰 문제 중 하나의 유명한 삽화 중 하나 인 식사 철학자 문제를 소개했습니다.
이 문제에는 5 명의 유명한 철학자가 원탁에 앉아 그릇에서 음식을 먹고 있습니다. 다섯 명의 철학자가 음식을 먹을 때 사용할 수있는 포크는 다섯 가지입니다. 그러나 철학자들은 음식을 먹기 위해 동시에 두 개의 포크를 사용하기로 결정했습니다.
이제 철학자들에게는 두 가지 주요 조건이 있습니다. 첫째, 각 철학자들은 먹거나 생각하는 상태에있을 수 있고 둘째, 그들은 먼저 포크, 즉 왼쪽과 오른쪽을 모두 얻어야합니다. 다섯 명의 철학자가 각각 동시에 왼쪽 포크를 고를 때 문제가 발생합니다. 이제 그들은 모두 올바른 포크가 무료가되기를 기다리고 있지만 음식을 먹고 올바른 포크를 사용할 수 없을 때까지 포크를 포기하지 않을 것입니다. 따라서 저녁 식사 테이블에 교착 상태가 발생합니다.
동시 시스템의 교착 상태
이제 보시면 동시 시스템에서도 동일한 문제가 발생할 수 있습니다. 위의 예에서 포크는 시스템 리소스이며 각 철학자는 리소스를 얻기 위해 경쟁하는 프로세스를 나타낼 수 있습니다.
Python 프로그램을 사용한 솔루션
이 문제의 해결책은 철학자들을 두 가지 유형으로 나누면 찾을 수 있습니다. greedy philosophers 과 generous philosophers. 주로 탐욕스러운 철학자는 왼쪽 포크를 집어 들고 거기에 올 때까지 기다릴 것입니다. 그런 다음 올바른 포크가있을 때까지 기다렸다가 집어 들고 먹고 내려 놓습니다. 반면에 관대 한 철학자는 왼쪽 포크를 집어 들려고 할 것이고, 그것이 없으면 잠시 후에 다시 시도 할 것입니다. 왼쪽 포크를 얻으면 오른쪽 포크를 얻으려고합니다. 그들이 올바른 포크를 얻게된다면 두 포크를 모두 먹고 놓을 것입니다. 그러나 오른쪽 포크를 얻지 못하면 왼쪽 포크를 해제합니다.
예
다음 Python 프로그램은 식사 철학자 문제에 대한 해결책을 찾는 데 도움이됩니다.
import threading
import random
import time
class DiningPhilosopher(threading.Thread):
running = True
def __init__(self, xname, Leftfork, Rightfork):
threading.Thread.__init__(self)
self.name = xname
self.Leftfork = Leftfork
self.Rightfork = Rightfork
def run(self):
while(self.running):
time.sleep( random.uniform(3,13))
print ('%s is hungry.' % self.name)
self.dine()
def dine(self):
fork1, fork2 = self.Leftfork, self.Rightfork
while self.running:
fork1.acquire(True)
locked = fork2.acquire(False)
if locked: break
fork1.release()
print ('%s swaps forks' % self.name)
fork1, fork2 = fork2, fork1
else:
return
self.dining()
fork2.release()
fork1.release()
def dining(self):
print ('%s starts eating '% self.name)
time.sleep(random.uniform(1,10))
print ('%s finishes eating and now thinking.' % self.name)
def Dining_Philosophers():
forks = [threading.Lock() for n in range(5)]
philosopherNames = ('1st','2nd','3rd','4th', '5th')
philosophers= [DiningPhilosopher(philosopherNames[i], forks[i%5], forks[(i+1)%5]) \
for i in range(5)]
random.seed()
DiningPhilosopher.running = True
for p in philosophers: p.start()
time.sleep(30)
DiningPhilosopher.running = False
print (" It is finishing.")
Dining_Philosophers()
위 프로그램은 탐욕스럽고 관대 한 철학자의 개념을 사용합니다. 이 프로그램은 또한acquire() 과 release() 의 방법 Lock 클래스 <threading>기준 치수. 다음 출력에서 솔루션을 볼 수 있습니다.
산출
4th is hungry.
4th starts eating
1st is hungry.
1st starts eating
2nd is hungry.
5th is hungry.
3rd is hungry.
1st finishes eating and now thinking.3rd swaps forks
2nd starts eating
4th finishes eating and now thinking.
3rd swaps forks5th starts eating
5th finishes eating and now thinking.
4th is hungry.
4th starts eating
2nd finishes eating and now thinking.
3rd swaps forks
1st is hungry.
1st starts eating
4th finishes eating and now thinking.
3rd starts eating
5th is hungry.
5th swaps forks
1st finishes eating and now thinking.
5th starts eating
2nd is hungry.
2nd swaps forks
4th is hungry.
5th finishes eating and now thinking.
3rd finishes eating and now thinking.
2nd starts eating 4th starts eating
It is finishing.
실생활에서 팀이 공통 작업을 수행하고 있다면 작업을 제대로 완료하기 위해 그들 사이에 의사 소통이 있어야합니다. 스레드에도 동일한 비유가 적용됩니다. 프로그래밍에서 프로세서의 이상적인 시간을 줄이기 위해 여러 스레드를 만들고 모든 스레드에 다른 하위 작업을 할당합니다. 따라서 통신 기능이 있어야하며 동기화 된 방식으로 작업을 완료하기 위해 서로 상호 작용해야합니다.
스레드 상호 통신과 관련된 다음 중요 사항을 고려하십시오.
No performance gain − 스레드와 프로세스 간의 적절한 통신을 달성 할 수없는 경우 동시성과 병렬성으로 인한 성능 향상은 소용이 없습니다.
Accomplish task properly − 스레드 간의 적절한 상호 통신 메커니즘이 없으면 할당 된 작업을 제대로 완료 할 수 없습니다.
More efficient than inter-process communication − 스레드 간 통신은 프로세스 내 모든 스레드가 동일한 주소 공간을 공유하고 공유 메모리를 사용할 필요가 없기 때문에 프로세스 간 통신보다 더 효율적이고 사용하기 쉽습니다.
스레드로부터 안전한 통신을위한 Python 데이터 구조
다중 스레드 코드는 한 스레드에서 다른 스레드로 정보를 전달하는 문제가 있습니다. 표준 커뮤니케이션 프리미티브는이 문제를 해결하지 못합니다. 따라서 통신 스레드로부터 안전한 통신을 위해 스레드간에 객체를 공유하려면 자체 복합 객체를 구현해야합니다. 다음은 몇 가지 데이터 구조를 변경 한 후 스레드로부터 안전한 통신을 제공합니다.
세트
스레드로부터 안전한 방식으로 집합 데이터 구조를 사용하려면 자체 잠금 메커니즘을 구현하기 위해 set 클래스를 확장해야합니다.
예
다음은 클래스를 확장하는 Python 예제입니다.
class extend_class(set):
def __init__(self, *args, **kwargs):
self._lock = Lock()
super(extend_class, self).__init__(*args, **kwargs)
def add(self, elem):
self._lock.acquire()
try:
super(extend_class, self).add(elem)
finally:
self._lock.release()
def delete(self, elem):
self._lock.acquire()
try:
super(extend_class, self).delete(elem)
finally:
self._lock.release()
위의 예에서 이름이 extend_class 파이썬에서 더 상속 된 정의되었습니다 set class. 이 클래스의 생성자 내에 잠금 객체가 생성됩니다. 이제 두 가지 기능이 있습니다.add() 과 delete(). 이러한 함수는 정의되어 있으며 스레드로부터 안전합니다. 그들은 둘 다에 의존합니다super 하나의 주요 예외가있는 클래스 기능.
데코레이터
스레드로부터 안전한 통신을위한 또 다른 핵심 방법은 데코레이터를 사용하는 것입니다.
예
데코레이터 & mminus;를 사용하는 방법을 보여주는 Python 예제를 고려하십시오.
def lock_decorator(method):
def new_deco_method(self, *args, **kwargs):
with self._lock:
return method(self, *args, **kwargs)
return new_deco_method
class Decorator_class(set):
def __init__(self, *args, **kwargs):
self._lock = Lock()
super(Decorator_class, self).__init__(*args, **kwargs)
@lock_decorator
def add(self, *args, **kwargs):
return super(Decorator_class, self).add(elem)
@lock_decorator
def delete(self, *args, **kwargs):
return super(Decorator_class, self).delete(elem)
위의 예에서 lock_decorator라는 데코레이터 메서드가 정의되어 Python 메서드 클래스에서 상속되었습니다. 그런 다음이 클래스의 생성자 내에 잠금 객체가 생성됩니다. 이제 add ()와 delete ()의 두 가지 함수가 있습니다. 이러한 함수는 정의되어 있으며 스레드로부터 안전합니다. 둘 다 하나의 주요 예외를 제외하고 수퍼 클래스 기능에 의존합니다.
기울기
목록 데이터 구조는 스레드로부터 안전하고 임시 메모리 내 저장을위한 빠르고 쉬운 구조입니다. Cpython에서 GIL은 이들에 대한 동시 액세스로부터 보호합니다. 목록이 스레드로부터 안전하다는 것을 알게되면서 목록에있는 데이터는 어떻습니까? 실제로 목록의 데이터는 보호되지 않습니다. 예를 들면L.append(x)다른 스레드가 동일한 작업을 시도하는 경우 예상 결과를 반환한다고 보장하지 않습니다. 왜냐하면append() 원자 적 연산이고 스레드로부터 안전하지만 다른 스레드는 목록의 데이터를 동시에 수정하려고 시도하므로 출력에서 경쟁 조건의 부작용을 볼 수 있습니다.
이러한 종류의 문제를 해결하고 데이터를 안전하게 수정하려면 적절한 잠금 메커니즘을 구현해야합니다.이를 통해 여러 스레드가 잠재적으로 경합 상태에 빠질 수 없도록 보장합니다. 적절한 잠금 메커니즘을 구현하기 위해 이전 예제에서했던 것처럼 클래스를 확장 할 수 있습니다.
목록에 대한 다른 원자 연산은 다음과 같습니다.
L.append(x)
L1.extend(L2)
x = L[i]
x = L.pop()
L1[i:j] = L2
L.sort()
x = y
x.field = y
D[x] = y
D1.update(D2)
D.keys()
여기-
- L, L1, L2는 모두 목록입니다.
- D, D1, D2는 dicts입니다.
- x, y는 객체입니다.
- i, j는 정수입니다.
대기열
목록의 데이터가 보호되지 않으면 결과에 직면해야 할 수도 있습니다. 경쟁 조건의 잘못된 데이터 항목을 가져 오거나 삭제할 수 있습니다. 그렇기 때문에 큐 데이터 구조를 사용하는 것이 좋습니다. 대기열의 실제 예는 차량이 먼저 진입하고 먼저 나가는 단일 차선 일방 통행 도로 일 수 있습니다. 매표소와 버스 정류장의 대기열에 대한 실제 사례를 더 많이 볼 수 있습니다.
큐는 기본적으로 스레드로부터 안전한 데이터 구조이며 복잡한 잠금 메커니즘 구현에 대해 걱정할 필요가 없습니다. 파이썬은 우리에게
대기열 유형
이 섹션에서는 다양한 유형의 대기열에 대해 알아 보겠습니다. Python은 다음에서 사용할 세 가지 대기열 옵션을 제공합니다.<queue> 모듈-
- 일반 대기열 (FIFO, 선입 선출)
- LIFO, 선입 선출
- Priority
이후 섹션에서 다른 대기열에 대해 알아 봅니다.
일반 대기열 (FIFO, 선입 선출)
Python에서 제공하는 가장 일반적으로 사용되는 큐 구현입니다. 이 대기열 메커니즘에서 먼저 오는 사람은 먼저 서비스를받습니다. FIFO는 일반 대기열이라고도합니다. FIFO 대기열은 다음과 같이 나타낼 수 있습니다.
FIFO 대기열의 Python 구현
파이썬에서 FIFO 대기열은 단일 스레드와 다중 스레드로 구현 될 수 있습니다.
단일 스레드가있는 FIFO 대기열
단일 스레드로 FIFO 대기열을 구현하려면 Queue클래스는 기본 선입 선출 컨테이너를 구현합니다. 요소는 다음을 사용하여 시퀀스의 한 "끝"에 추가됩니다.put()을 사용하여 다른 쪽 끝에서 제거 get().
예
다음은 단일 스레드로 FIFO 대기열을 구현하기위한 Python 프로그램입니다.
import queue
q = queue.Queue()
for i in range(8):
q.put("item-" + str(i))
while not q.empty():
print (q.get(), end = " ")
산출
item-0 item-1 item-2 item-3 item-4 item-5 item-6 item-7
출력은 위의 프로그램이 단일 스레드를 사용하여 요소가 삽입 된 순서대로 큐에서 제거되었음을 보여줍니다.
여러 스레드가있는 FIFO 대기열
다중 스레드로 FIFO를 구현하려면 큐 모듈에서 확장 된 myqueue () 함수를 정의해야합니다. get () 및 put () 메서드의 작동은 단일 스레드로 FIFO 대기열을 구현하는 동안 위에서 설명한 것과 동일합니다. 그런 다음 다중 스레드로 만들려면 스레드를 선언하고 인스턴스화해야합니다. 이러한 스레드는 FIFO 방식으로 대기열을 소비합니다.
예
다음은 여러 스레드가있는 FIFO 대기열 구현을위한 Python 프로그램입니다.
import threading
import queue
import random
import time
def myqueue(queue):
while not queue.empty():
item = queue.get()
if item is None:
break
print("{} removed {} from the queue".format(threading.current_thread(), item))
queue.task_done()
time.sleep(2)
q = queue.Queue()
for i in range(5):
q.put(i)
threads = []
for i in range(4):
thread = threading.Thread(target=myqueue, args=(q,))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
산출
<Thread(Thread-3654, started 5044)> removed 0 from the queue
<Thread(Thread-3655, started 3144)> removed 1 from the queue
<Thread(Thread-3656, started 6996)> removed 2 from the queue
<Thread(Thread-3657, started 2672)> removed 3 from the queue
<Thread(Thread-3654, started 5044)> removed 4 from the queue
LIFO, Last in First Out 대기열
이 대기열은 FIFO (선입 선출) 대기열과 완전히 반대되는 비유를 사용합니다. 이 대기열 메커니즘에서는 마지막에 오는 사람이 먼저 서비스를받습니다. 이것은 스택 데이터 구조를 구현하는 것과 유사합니다. LIFO 대기열은 인공 지능 알고리즘과 같은 깊이 우선 검색을 구현하는 동안 유용합니다.
LIFO 대기열의 Python 구현
파이썬에서 LIFO 대기열은 단일 스레드 및 다중 스레드로 구현 될 수 있습니다.
단일 스레드가있는 LIFO 대기열
단일 스레드로 LIFO 대기열을 구현하려면 Queue 클래스는 구조를 사용하여 기본 후입 선출 컨테이너를 구현합니다. Queue.LifoQueue. 자, 부름에put(), 요소는 컨테이너의 헤드에 추가되고 사용시에도 헤드에서 제거됩니다. get().
예
다음은 단일 스레드로 LIFO 대기열을 구현하기위한 Python 프로그램입니다.
import queue
q = queue.LifoQueue()
for i in range(8):
q.put("item-" + str(i))
while not q.empty():
print (q.get(), end=" ")
Output:
item-7 item-6 item-5 item-4 item-3 item-2 item-1 item-0
출력은 위의 프로그램이 단일 스레드를 사용하여 요소가 삽입 된 순서와 반대로 큐에서 제거되었음을 보여줍니다.
여러 스레드가있는 LIFO 대기열
구현은 다중 스레드로 FIFO 대기열을 구현 한 것과 유사합니다. 유일한 차이점은Queue 구조를 사용하여 기본 후입 선출 컨테이너를 구현할 클래스 Queue.LifoQueue.
예
다음은 여러 스레드가있는 LIFO 대기열을 구현하기위한 Python 프로그램입니다.
import threading
import queue
import random
import time
def myqueue(queue):
while not queue.empty():
item = queue.get()
if item is None:
break
print("{} removed {} from the queue".format(threading.current_thread(), item))
queue.task_done()
time.sleep(2)
q = queue.LifoQueue()
for i in range(5):
q.put(i)
threads = []
for i in range(4):
thread = threading.Thread(target=myqueue, args=(q,))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
산출
<Thread(Thread-3882, started 4928)> removed 4 from the queue
<Thread(Thread-3883, started 4364)> removed 3 from the queue
<Thread(Thread-3884, started 6908)> removed 2 from the queue
<Thread(Thread-3885, started 3584)> removed 1 from the queue
<Thread(Thread-3882, started 4928)> removed 0 from the queue
우선 순위 대기열
FIFO 및 LIFO 대기열에서 항목 순서는 삽입 순서와 관련이 있습니다. 그러나 삽입 순서보다 우선 순위가 더 중요한 경우가 많습니다. 실제 사례를 살펴 보겠습니다. 공항의 보안이 다른 범주의 사람들을 확인하고 있다고 가정합니다. VVIP의 사람, 항공사 직원, 세관 직원, 카테고리는 일반인과 마찬가지로 도착 기준으로 확인되는 대신 우선적으로 확인 될 수 있습니다.
우선 순위 대기열에 대해 고려해야 할 또 다른 중요한 측면은 작업 스케줄러를 개발하는 방법입니다. 한 가지 일반적인 설계는 대기열에서 우선 순위에 따라 가장 많은 상담원 작업을 제공하는 것입니다. 이 데이터 구조는 우선 순위 값에 따라 큐에서 항목을 선택하는 데 사용할 수 있습니다.
우선 순위 대기열의 Python 구현
파이썬에서 우선 순위 큐는 단일 스레드와 다중 스레드로 구현 될 수 있습니다.
단일 스레드가있는 우선 순위 대기열
단일 스레드로 우선 순위 대기열을 구현하려면 Queue 클래스는 구조를 사용하여 우선 순위 컨테이너에 작업을 구현합니다. Queue.PriorityQueue. 자, 부름에put(), 가장 낮은 값이 가장 높은 우선 순위를 갖는 값으로 요소가 추가되므로 다음을 사용하여 먼저 검색됩니다. get().
예
단일 스레드로 우선 순위 대기열을 구현하기 위해 다음 Python 프로그램을 고려하십시오.
import queue as Q
p_queue = Q.PriorityQueue()
p_queue.put((2, 'Urgent'))
p_queue.put((1, 'Most Urgent'))
p_queue.put((10, 'Nothing important'))
prio_queue.put((5, 'Important'))
while not p_queue.empty():
item = p_queue.get()
print('%s - %s' % item)
산출
1 – Most Urgent
2 - Urgent
5 - Important
10 – Nothing important
위의 출력에서 큐가 우선 순위에 따라 항목을 저장했음을 알 수 있습니다. 값이 적을수록 우선 순위가 높습니다.
다중 스레드가있는 우선 순위 대기열
구현은 다중 스레드가있는 FIFO 및 LIFO 대기열의 구현과 유사합니다. 유일한 차이점은Queue 구조를 사용하여 우선 순위를 초기화하는 클래스 Queue.PriorityQueue. 또 다른 차이점은 큐가 생성되는 방식입니다. 아래 주어진 예에서는 두 개의 동일한 데이터 세트로 생성됩니다.
예
다음 Python 프로그램은 다중 스레드가있는 우선 순위 대기열의 구현에 도움이됩니다.
import threading
import queue
import random
import time
def myqueue(queue):
while not queue.empty():
item = queue.get()
if item is None:
break
print("{} removed {} from the queue".format(threading.current_thread(), item))
queue.task_done()
time.sleep(1)
q = queue.PriorityQueue()
for i in range(5):
q.put(i,1)
for i in range(5):
q.put(i,1)
threads = []
for i in range(2):
thread = threading.Thread(target=myqueue, args=(q,))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
산출
<Thread(Thread-4939, started 2420)> removed 0 from the queue
<Thread(Thread-4940, started 3284)> removed 0 from the queue
<Thread(Thread-4939, started 2420)> removed 1 from the queue
<Thread(Thread-4940, started 3284)> removed 1 from the queue
<Thread(Thread-4939, started 2420)> removed 2 from the queue
<Thread(Thread-4940, started 3284)> removed 2 from the queue
<Thread(Thread-4939, started 2420)> removed 3 from the queue
<Thread(Thread-4940, started 3284)> removed 3 from the queue
<Thread(Thread-4939, started 2420)> removed 4 from the queue
<Thread(Thread-4940, started 3284)> removed 4 from the queue
이 장에서는 스레드 애플리케이션 테스트에 대해 배웁니다. 우리는 또한 테스트의 중요성을 배울 것입니다.
테스트해야하는 이유
테스트의 중요성에 대한 논의를 시작하기 전에 테스트가 무엇인지 알아야합니다. 일반적으로 테스트는 어떤 것이 얼마나 잘 작동하는지 알아내는 기술입니다. 반면에 특히 컴퓨터 프로그램이나 소프트웨어에 대해 이야기하는 경우 테스트는 소프트웨어 프로그램의 기능에 액세스하는 기술입니다.
이 섹션에서는 소프트웨어 테스트의 중요성에 대해 설명합니다. 소프트웨어 개발에서는 소프트웨어를 클라이언트에 배포하기 전에 다시 확인해야합니다. 그렇기 때문에 숙련 된 테스트 팀이 소프트웨어를 테스트하는 것이 매우 중요합니다. 소프트웨어 테스트의 중요성을 이해하려면 다음 사항을 고려하십시오.
소프트웨어 품질 향상
확실히, 어떤 회사도 저품질 소프트웨어를 제공하고 싶어하지 않으며 어떤 고객도 저품질 소프트웨어를 구입하고 싶어하지 않습니다. 테스트는 버그를 찾고 수정하여 소프트웨어의 품질을 향상시킵니다.
고객 만족
모든 비즈니스에서 가장 중요한 부분은 고객 만족입니다. 버그가없고 양질의 소프트웨어를 제공함으로써 회사는 고객 만족을 달성 할 수 있습니다.
새로운 기능의 영향 감소
10000 라인의 소프트웨어 시스템을 만들고 새 기능을 추가해야한다고 가정하면 개발 팀은이 새 기능이 전체 소프트웨어에 미치는 영향에 대해 우려 할 것입니다. 여기서도 테스트는 중요한 역할을합니다. 테스트 팀이 좋은 테스트 모음을 만들면 잠재적 인 치명적인 중단으로부터 우리를 구할 수 있기 때문입니다.
사용자 경험
모든 비즈니스에서 가장 중요한 또 다른 부분은 해당 제품 사용자의 경험입니다. 테스트를 통해서만 최종 사용자가 제품을 간단하고 쉽게 사용할 수 있음을 확인할 수 있습니다.
비용 절감
테스트는 배포 후 수정하는 대신 개발 테스트 단계에서 버그를 찾아 수정함으로써 소프트웨어의 총 비용을 줄일 수 있습니다. 소프트웨어 제공 후 중대한 버그가 발생하면 비용 측면에서 말하면 유형 비용이 증가하고 고객 불만, 회사의 부정적인 평판 등으로 말하면 무형 비용이 증가합니다.
무엇을 테스트해야합니까?
항상 테스트 대상에 대한 적절한 지식을 가지고있는 것이 좋습니다. 이 섹션에서는 소프트웨어를 테스트하는 동안 테스터의 주된 동기가 무엇인지 먼저 이해합니다. 코드 커버리지, 즉 테스트하는 동안 테스트 스위트가 히트 한 코드 라인 수는 피해야합니다. 테스트하는 동안 코드 줄 수에만 집중하면 시스템에 실제 가치가 추가되지 않기 때문입니다. 일부 버그가 남아있을 수 있으며 배포 후에도 나중에 반영됩니다.
테스트 대상과 관련된 다음과 같은 중요한 사항을 고려하십시오.
코드 커버리지보다는 코드의 기능 테스트에 집중해야합니다.
먼저 코드의 가장 중요한 부분을 테스트 한 다음 코드에서 덜 중요한 부분으로 이동해야합니다. 확실히 시간을 절약 할 수 있습니다.
테스터는 소프트웨어를 한계까지 밀어 붙일 수있는 다양한 테스트를 거쳐야합니다.
동시 소프트웨어 프로그램 테스트를위한 접근 방식
멀티 코어 아키텍처의 진정한 기능을 활용하는 기능으로 인해 동시 소프트웨어 시스템이 순차 시스템을 대체하고 있습니다. 최근에는 휴대 전화부터 세탁기, 자동차, 비행기 등 모든 분야에서 동시 시스템 프로그램이 사용되고 있습니다. 단일 스레드 응용 프로그램에 여러 스레드를 추가하면 이미 버그라면 여러 버그로 끝날 것입니다.
동시 소프트웨어 프로그램에 대한 테스트 기술은 경쟁 조건, 교착 상태 및 원 자성 위반과 같은 잠재적으로 유해한 패턴을 노출하는 인터리빙을 선택하는 데 광범위하게 초점을 맞추고 있습니다. 다음은 동시 소프트웨어 프로그램을 테스트하는 두 가지 방법입니다.
체계적인 탐색
이 접근 방식은 가능한 한 광범위하게 인터리빙 공간을 탐색하는 것을 목표로합니다. 이러한 접근 방식은 무차별 대입 기술을 채택 할 수 있고 다른 접근 방식은 부분 순서 감소 기술 또는 휴리스틱 기술을 채택하여 인터리빙 공간을 탐색 할 수 있습니다.
부동산 중심
속성 기반 접근 방식은 의심스러운 메모리 액세스 패턴과 같은 특정 속성을 노출하는 인터리빙에서 동시성 오류가 발생할 가능성이 더 높다는 관찰에 의존합니다. 서로 다른 속성 기반 접근 방식은 경쟁 조건, 교착 상태 및 원 자성 위반과 같은 다양한 결함을 대상으로하며 이는 하나 또는 다른 특정 속성에 더 의존합니다.
테스트 전략
테스트 전략은 테스트 접근 방식이라고도합니다. 전략은 테스트가 수행되는 방법을 정의합니다. 테스트 접근 방식에는 두 가지 기술이 있습니다.
사전 예방
빌드가 생성되기 전에 결함을 찾아 수정하기 위해 테스트 설계 프로세스를 가능한 한 빨리 시작하는 접근 방식입니다.
반응성
개발 프로세스가 완료 될 때까지 테스트가 시작되지 않는 접근 방식입니다.
파이썬 프로그램에 테스트 전략이나 접근 방식을 적용하기 전에 소프트웨어 프로그램이 가질 수있는 오류의 종류에 대한 기본적인 아이디어가 있어야합니다. 오류는 다음과 같습니다-
구문 오류
프로그램 개발 중에 많은 작은 오류가있을 수 있습니다. 오류는 대부분 입력 실수로 인해 발생합니다. 예를 들어, 콜론 누락 또는 키워드 철자 오류 등이 있습니다. 이러한 오류는 논리가 아닌 프로그램 구문의 오류로 인한 것입니다. 따라서 이러한 오류를 구문 오류라고합니다.
의미 오류
의미 오류는 논리 오류라고도합니다. 소프트웨어 프로그램에 논리적 또는 의미 적 오류가있는 경우 명령문은 올바르게 컴파일되고 실행되지만 로직이 올바르지 않기 때문에 원하는 출력을 제공하지 않습니다.
단위 테스트
이것은 파이썬 프로그램을 테스트하기 위해 가장 많이 사용되는 테스트 전략 중 하나입니다. 이 전략은 코드의 단위 또는 구성 요소를 테스트하는 데 사용됩니다. 단위 또는 구성 요소는 코드의 클래스 또는 기능을 의미합니다. 단위 테스트는 "작은"단위를 테스트하여 대규모 프로그래밍 시스템의 테스트를 단순화합니다. 위의 개념의 도움으로 단위 테스트는 소스 코드의 개별 단위가 원하는 출력을 반환하는지 확인하기 위해 테스트하는 방법으로 정의 될 수 있습니다.
이후 섹션에서는 단위 테스트를위한 다양한 Python 모듈에 대해 알아 봅니다.
unittest 모듈
단위 테스트를위한 첫 번째 모듈은 unittest 모듈입니다. JUnit에서 영감을 얻었으며 기본적으로 Python3.6에 포함되어 있습니다. 테스트 자동화, 테스트를위한 설정 및 종료 코드 공유, 테스트를 컬렉션으로 집계,보고 프레임 워크에서 테스트의 독립성을 지원합니다.
다음은 unittest 모듈에서 지원하는 몇 가지 중요한 개념입니다.
텍스트 고정 장치
테스트를 시작하기 전에 실행하고 테스트가 끝나면 해체 할 수 있도록 테스트를 설정하는 데 사용됩니다. 테스트를 시작하기 전에 필요한 임시 데이터베이스, 디렉토리 등의 생성이 포함될 수 있습니다.
테스트 케이스
테스트 케이스는 필요한 응답이 특정 입력 세트에서 오는지 여부를 확인합니다. unittest 모듈에는 새 테스트 케이스를 만드는 데 사용할 수있는 TestCase라는 기본 클래스가 포함되어 있습니다. 그것은 기본적으로 두 가지 방법을 포함합니다-
setUp()− 시험하기 전에 테스트 픽스처를 설정하는 후크 방법. 구현 된 테스트 메서드를 호출하기 전에 호출됩니다.
tearDown( − 클래스의 모든 테스트를 실행 한 후 클래스 픽스처를 분해하기위한 후크 메서드.
테스트 스위트
테스트 스위트, 테스트 케이스 또는 둘 다의 모음입니다.
테스트 러너
테스트 케이스 또는 슈트의 실행을 제어하고 사용자에게 결과를 제공합니다. 결과를 제공하기 위해 GUI 또는 간단한 텍스트 인터페이스를 사용할 수 있습니다.
Example
다음 Python 프로그램은 unittest 모듈을 사용하여 Fibonacci. 이 프로그램은 피보나치 수열을 계산하는 데 도움이됩니다. 이 예제에서는 다른 메서드를 사용하여 테스트 케이스를 정의하기 위해 Fibo_test라는 클래스를 만들었습니다. 이러한 메서드는 unittest.TestCase에서 상속됩니다. 기본적으로 setUp ()과 tearDown ()이라는 두 가지 메서드를 사용하고 있습니다. 우리는 또한 testfibocal 방법을 정의합니다. 테스트 이름은 문자 테스트로 시작해야합니다. 마지막 블록에서 unittest.main ()은 테스트 스크립트에 대한 명령 줄 인터페이스를 제공합니다.
import unittest
def fibonacci(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return a
class Fibo_Test(unittest.TestCase):
def setUp(self):
print("This is run before our tests would be executed")
def tearDown(self):
print("This is run after the completion of execution of our tests")
def testfibocal(self):
self.assertEqual(fib(0), 0)
self.assertEqual(fib(1), 1)
self.assertEqual(fib(5), 5)
self.assertEqual(fib(10), 55)
self.assertEqual(fib(20), 6765)
if __name__ == "__main__":
unittest.main()
명령 줄에서 실행하면 위 스크립트는 다음과 같은 출력을 생성합니다.
산출
This runs before our tests would be executed.
This runs after the completion of execution of our tests.
.
----------------------------------------------------------------------
Ran 1 test in 0.006s
OK
이제 더 명확하게하기 위해 피보나치 모듈을 정의하는 데 도움이되는 코드를 변경하고 있습니다.
예를 들어 다음 코드 블록을 고려하십시오-
def fibonacci(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return a
코드 블록에 대한 몇 가지 변경 사항은 다음과 같습니다.
def fibonacci(n):
a, b = 1, 1
for i in range(n):
a, b = b, a + b
return a
이제 변경된 코드로 스크립트를 실행하면 다음과 같은 출력이 표시됩니다.
This runs before our tests would be executed.
This runs after the completion of execution of our tests.
F
======================================================================
FAIL: testCalculation (__main__.Fibo_Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "unitg.py", line 15, in testCalculation
self.assertEqual(fib(0), 0)
AssertionError: 1 != 0
----------------------------------------------------------------------
Ran 1 test in 0.007s
FAILED (failures = 1)
위의 출력은 모듈이 원하는 출력을 제공하지 못했음을 보여줍니다.
Docktest 모듈
docktest 모듈은 단위 테스트에도 도움이됩니다. 또한 파이썬과 함께 미리 패키지로 제공됩니다. unittest 모듈보다 사용하기 쉽습니다. unittest 모듈은 복잡한 테스트에 더 적합합니다. doctest 모듈을 사용하려면 가져와야합니다. 해당 함수의 독 스트링에는 출력과 함께 대화 형 파이썬 세션이 있어야합니다.
코드에서 모든 것이 정상이면 docktest 모듈에서 출력이 없습니다. 그렇지 않으면 출력을 제공합니다.
예
다음 Python 예제는 docktest 모듈을 사용하여 Fibonacci라는 모듈을 테스트합니다.이 모듈은 숫자의 Fibonacci 시리즈를 계산하는 데 도움이됩니다.
import doctest
def fibonacci(n):
"""
Calculates the Fibonacci number
>>> fibonacci(0)
0
>>> fibonacci(1)
1
>>> fibonacci(10)
55
>>> fibonacci(20)
6765
>>>
"""
a, b = 1, 1
for i in range(n):
a, b = b, a + b
return a
if __name__ == "__main__":
doctest.testmod()
fib라는 해당 함수의 독 스트링에 출력과 함께 대화 형 파이썬 세션이 있음을 알 수 있습니다. 우리 코드가 괜찮다면 doctest 모듈에서 출력이 없을 것입니다. 하지만 어떻게 작동하는지보기 위해 -v 옵션을 사용하여 실행할 수 있습니다.
(base) D:\ProgramData>python dock_test.py -v
Trying:
fibonacci(0)
Expecting:
0
ok
Trying:
fibonacci(1)
Expecting:
1
ok
Trying:
fibonacci(10)
Expecting:
55
ok
Trying:
fibonacci(20)
Expecting:
6765
ok
1 items had no tests:
__main__
1 items passed all tests:
4 tests in __main__.fibonacci
4 tests in 2 items.
4 passed and 0 failed.
Test passed.
이제 피보나치 모듈을 정의하는 데 도움이되는 코드를 변경하겠습니다.
예를 들어 다음 코드 블록을 고려하십시오-
def fibonacci(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return a
다음 코드 블록은 변경에 도움이됩니다.
def fibonacci(n):
a, b = 1, 1
for i in range(n):
a, b = b, a + b
return a
-v 옵션 없이도 변경된 코드로 스크립트를 실행하면 아래와 같이 출력됩니다.
산출
(base) D:\ProgramData>python dock_test.py
**********************************************************************
File "unitg.py", line 6, in __main__.fibonacci
Failed example:
fibonacci(0)
Expected:
0
Got:
1
**********************************************************************
File "unitg.py", line 10, in __main__.fibonacci
Failed example:
fibonacci(10)
Expected:
55
Got:
89
**********************************************************************
File "unitg.py", line 12, in __main__.fibonacci
Failed example:
fibonacci(20)
Expected:
6765
Got:
10946
**********************************************************************
1 items had failures:
3 of 4 in __main__.fibonacci
***Test Failed*** 3 failures.
위의 출력에서 세 가지 테스트가 실패했음을 알 수 있습니다.
이 장에서는 스레드 애플리케이션을 디버깅하는 방법을 배웁니다. 또한 디버깅의 중요성에 대해서도 배웁니다.
디버깅이란?
컴퓨터 프로그래밍에서 디버깅은 컴퓨터 프로그램에서 버그, 오류 및 이상을 찾아 제거하는 프로세스입니다. 이 프로세스는 코드가 작성 되 자마자 시작되고 코드가 다른 프로그래밍 단위와 결합되어 소프트웨어 제품을 형성함에 따라 연속 단계에서 계속됩니다. 디버깅은 소프트웨어 테스트 프로세스의 일부이며 전체 소프트웨어 개발 라이프 사이클의 필수적인 부분입니다.
Python 디버거
Python 디버거 또는 pdbPython 표준 라이브러리의 일부입니다. 찾기 어려운 버그를 추적하기위한 좋은 대체 도구이며 결함이있는 코드를 빠르고 안정적으로 수정할 수 있습니다. 다음은 가장 중요한 두 가지 작업입니다.pdp 디버거-
- 런타임에 변수 값을 확인할 수 있습니다.
- 코드를 단계별로 실행하고 중단 점을 설정할 수도 있습니다.
다음 두 가지 방법으로 pdb를 사용할 수 있습니다.
- 명령 줄을 통해; 이를 사후 디버깅이라고도합니다.
- 대화식으로 pdb를 실행합니다.
pdb 작업
Python 디버거로 작업하려면 디버거에 침입하려는 위치에서 다음 코드를 사용해야합니다.
import pdb;
pdb.set_trace()
명령 줄을 통해 pdb로 작업하려면 다음 명령을 고려하십시오.
- h(help)
- d(down)
- u(up)
- b(break)
- cl(clear)
- l(list))
- n(next))
- c(continue)
- s(step)
- r(return))
- b(break)
다음은 Python 디버거의 h (help) 명령 데모입니다.
import pdb
pdb.set_trace()
--Call--
>d:\programdata\lib\site-packages\ipython\core\displayhook.py(247)__call__()
-> def __call__(self, result = None):
(Pdb) h
Documented commands (type help <topic>):
========================================
EOF c d h list q rv undisplay
a cl debug help ll quit s unt
alias clear disable ignore longlist r source until
args commands display interact n restart step up
b condition down j next return tbreak w
break cont enable jump p retval u whatis
bt continue exit l pp run unalias where
Miscellaneous help topics:
==========================
exec pdb
예
Python 디버거로 작업하는 동안 다음 줄을 사용하여 스크립트의 아무 곳에 나 중단 점을 설정할 수 있습니다.
import pdb;
pdb.set_trace()
중단 점을 설정 한 후 스크립트를 정상적으로 실행할 수 있습니다. 스크립트는 특정 지점까지 실행됩니다. 선이 설정 될 때까지. 스크립트의 여러 위치에서 위에서 언급 한 줄을 사용하여 스크립트를 실행하는 다음 예제를 고려하십시오.
import pdb;
a = "aaa"
pdb.set_trace()
b = "bbb"
c = "ccc"
final = a + b + c
print (final)
위의 스크립트가 실행되면 a =“aaa”까지 프로그램이 실행됩니다. 다음 출력에서이를 확인할 수 있습니다.
산출
--Return--
> <ipython-input-7-8a7d1b5cc854>(3)<module>()->None
-> pdb.set_trace()
(Pdb) p a
'aaa'
(Pdb) p b
*** NameError: name 'b' is not defined
(Pdb) p c
*** NameError: name 'c' is not defined
pdb에서 'p (print)'명령을 사용한 후이 스크립트는 'aaa'만 출력합니다. a = "aaa"까지 중단 점을 설정했기 때문에 오류가 발생합니다.
유사하게, 중단 점을 변경하여 스크립트를 실행하고 출력의 차이를 볼 수 있습니다.
import pdb
a = "aaa"
b = "bbb"
c = "ccc"
pdb.set_trace()
final = a + b + c
print (final)
산출
--Return--
> <ipython-input-9-a59ef5caf723>(5)<module>()->None
-> pdb.set_trace()
(Pdb) p a
'aaa'
(Pdb) p b
'bbb'
(Pdb) p c
'ccc'
(Pdb) p final
*** NameError: name 'final' is not defined
(Pdb) exit
다음 스크립트에서 우리는 프로그램의 마지막 줄에 중단 점을 설정합니다.
import pdb
a = "aaa"
b = "bbb"
c = "ccc"
final = a + b + c
pdb.set_trace()
print (final)
출력은 다음과 같습니다.
--Return--
> <ipython-input-11-8019b029997d>(6)<module>()->None
-> pdb.set_trace()
(Pdb) p a
'aaa'
(Pdb) p b
'bbb'
(Pdb) p c
'ccc'
(Pdb) p final
'aaabbbccc'
(Pdb)
이 장에서는 벤치마킹과 프로파일 링이 성능 문제를 해결하는 데 어떻게 도움이되는지 알아 봅니다.
코드를 작성했고 원하는 결과도 제공한다고 가정 해 보겠습니다. 그러나 요구 사항이 변경 되었기 때문에이 코드를 조금 더 빠르게 실행하려면 어떻게해야합니까? 이 경우 코드의 어떤 부분이 전체 프로그램 속도를 늦추는 지 알아 내야합니다. 이 경우 벤치마킹 및 프로파일 링이 유용 할 수 있습니다.
벤치마킹이란 무엇입니까?
벤치마킹은 표준과 비교하여 무언가를 평가하는 것을 목표로합니다. 그러나 여기서 제기되는 질문은 벤치마킹이 무엇이고 소프트웨어 프로그래밍의 경우 왜 필요한지입니다. 코드 벤치마킹은 코드 실행 속도와 병목 지점을 의미합니다. 벤치마킹의 주요 이유 중 하나는 코드를 최적화하기 때문입니다.
벤치마킹은 어떻게 작동합니까?
벤치마킹 작업에 대해 이야기한다면 전체 프로그램을 하나의 현재 상태로 벤치마킹하는 것으로 시작해야합니다. 그런 다음 마이크로 벤치 마크를 결합한 다음 프로그램을 더 작은 프로그램으로 분해 할 수 있습니다. 프로그램 내 병목 현상을 찾아 최적화하기 위해. 즉, 크고 어려운 문제를 최적화를 위해 일련의 작고 조금 더 쉬운 문제로 나누는 것으로 이해할 수 있습니다.
벤치마킹을위한 Python 모듈
Python에는 기본적으로 벤치마킹을위한 모듈이 있습니다. timeit. 의 도움으로timeit 모듈을 사용하면 메인 프로그램 내에서 작은 Python 코드의 성능을 측정 할 수 있습니다.
예
다음 Python 스크립트에서 timeit 두 기능을 실행하는 데 걸리는 시간을 추가로 측정하는 모듈 – functionA 과 functionB −
import timeit
import time
def functionA():
print("Function A starts the execution:")
print("Function A completes the execution:")
def functionB():
print("Function B starts the execution")
print("Function B completes the execution")
start_time = timeit.default_timer()
functionA()
print(timeit.default_timer() - start_time)
start_time = timeit.default_timer()
functionB()
print(timeit.default_timer() - start_time)
위의 스크립트를 실행하면 아래와 같이 두 함수의 실행 시간을 얻습니다.
산출
Function A starts the execution:
Function A completes the execution:
0.0014599495514175942
Function B starts the execution
Function B completes the execution
0.0017024724827479076
데코레이터 기능을 사용하여 자체 타이머 작성
파이썬에서는 우리 자신의 타이머를 만들 수 있습니다. timeit기준 치수. 의 도움으로 수행 할 수 있습니다.decorator함수. 다음은 사용자 지정 타이머의 예입니다.
import random
import time
def timer_func(func):
def function_timer(*args, **kwargs):
start = time.time()
value = func(*args, **kwargs)
end = time.time()
runtime = end - start
msg = "{func} took {time} seconds to complete its execution."
print(msg.format(func = func.__name__,time = runtime))
return value
return function_timer
@timer_func
def Myfunction():
for x in range(5):
sleep_time = random.choice(range(1,3))
time.sleep(sleep_time)
if __name__ == '__main__':
Myfunction()
위의 파이썬 스크립트는 임의의 시간 모듈을 가져 오는 데 도움이됩니다. timer_func () 데코레이터 함수를 만들었습니다. 내부에 function_timer () 함수가 있습니다. 이제 중첩 된 함수는 전달 된 함수를 호출하기 전에 시간을 가져옵니다. 그런 다음 함수가 반환 될 때까지 기다렸다가 종료 시간을 가져옵니다. 이런 식으로 마침내 파이썬 스크립트가 실행 시간을 인쇄하도록 만들 수 있습니다. 스크립트는 아래와 같이 출력을 생성합니다.
산출
Myfunction took 8.000457763671875 seconds to complete its execution.
프로파일 링이란 무엇입니까?
때때로 프로그래머는 메모리 사용, 시간 복잡성 또는 프로그램에 대한 특정 명령 사용과 같은 일부 속성을 측정하여 해당 프로그램의 실제 기능을 측정하려고합니다. 이러한 프로그램에 대한 측정을 프로파일 링이라고합니다. 프로파일 링은 동적 프로그램 분석을 사용하여 이러한 측정을 수행합니다.
다음 섹션에서는 프로파일 링을위한 다양한 Python 모듈에 대해 알아 봅니다.
cProfile – 내장 모듈
cProfile프로파일 링을위한 Python 내장 모듈입니다. 모듈은 장기 실행 프로그램을 프로파일 링하는 데 적합하도록 합리적인 오버 헤드가있는 C 확장입니다. 실행 후 모든 기능과 실행 시간을 기록합니다. 매우 강력하지만 때로는 해석하고 실행하기가 약간 어렵습니다. 다음 예에서는 아래 코드에서 cProfile을 사용하고 있습니다.
예
def increment_global():
global x
x += 1
def taskofThread(lock):
for _ in range(50000):
lock.acquire()
increment_global()
lock.release()
def main():
global x
x = 0
lock = threading.Lock()
t1 = threading.Thread(target=taskofThread, args=(lock,))
t2 = threading.Thread(target= taskofThread, args=(lock,))
t1.start()
t2.start()
t1.join()
t2.join()
if __name__ == "__main__":
for i in range(5):
main()
print("x = {1} after Iteration {0}".format(i,x))
위의 코드는 thread_increment.py파일. 이제 다음과 같이 명령 줄에서 cProfile로 코드를 실행합니다.
(base) D:\ProgramData>python -m cProfile thread_increment.py
x = 100000 after Iteration 0
x = 100000 after Iteration 1
x = 100000 after Iteration 2
x = 100000 after Iteration 3
x = 100000 after Iteration 4
3577 function calls (3522 primitive calls) in 1.688 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
5 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap>:103(release)
5 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap>:143(__init__)
5 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap>:147(__enter__)
… … … …
위의 출력에서 cProfile이 호출 된 모든 3577 함수를 각각에 소요 된 시간과 호출 된 횟수와 함께 인쇄한다는 것이 분명합니다. 다음은 우리가 출력 한 열입니다.
ncalls -전화를 건 횟수입니다.
tottime − 주어진 함수에 소요 된 총 시간입니다.
percall − ncalls로 나눈 tottime의 몫을 나타냅니다.
cumtime−이 기능과 모든 하위 기능에 소요 된 누적 시간입니다. 재귀 함수의 경우에도 정확합니다.
percall − 원시 호출로 나눈 cumtime의 몫입니다.
filename:lineno(function) − 기본적으로 각 기능별 데이터를 제공합니다.
다중 스레드 작업을 위해 많은 수의 스레드를 만들어야한다고 가정합니다. 너무 많은 스레드로 인해 성능 문제가 많을 수 있으므로 계산 비용이 가장 많이 듭니다. 주요 문제는 처리량이 제한되는 것일 수 있습니다. 스레드 풀을 만들어이 문제를 해결할 수 있습니다. 스레드 풀은 작업을받을 준비가 된 사전 인스턴스화 및 유휴 스레드 그룹으로 정의 될 수 있습니다. 많은 수의 작업을 수행해야 할 때 모든 작업에 대해 새 스레드를 인스턴스화하는 것보다 스레드 풀을 만드는 것이 좋습니다. 스레드 풀은 다음과 같이 많은 스레드의 동시 실행을 관리 할 수 있습니다.
스레드 풀의 스레드가 실행을 완료하면 해당 스레드를 재사용 할 수 있습니다.
스레드가 종료되면 해당 스레드를 대체하기 위해 다른 스레드가 생성됩니다.
Python 모듈 – Concurrent.futures
Python 표준 라이브러리에는 concurrent.futures기준 치수. 이 모듈은 개발자에게 비동기 작업을 시작하기위한 고급 인터페이스를 제공하기 위해 Python 3.2에 추가되었습니다. 스레드 또는 프로세스 풀을 사용하여 작업을 실행하기위한 인터페이스를 제공하기위한 Python의 스레딩 및 다중 처리 모듈 상단에있는 추상화 계층입니다.
이후 섹션에서는 concurrent.futures 모듈의 다양한 클래스에 대해 알아 봅니다.
Executor 클래스
Executor의 추상 클래스입니다 concurrent.futuresPython 모듈. 직접 사용할 수 없으며 다음 구체적인 하위 클래스 중 하나를 사용해야합니다.
- ThreadPoolExecutor
- ProcessPoolExecutor
ThreadPoolExecutor – 구체적인 하위 클래스
Executor 클래스의 구체적인 하위 클래스 중 하나입니다. 서브 클래스는 멀티 스레딩을 사용하며 작업 제출을위한 스레드 풀을 얻습니다. 이 풀은 사용 가능한 스레드에 작업을 할당하고 실행되도록 예약합니다.
ThreadPoolExecutor를 만드는 방법은 무엇입니까?
의 도움으로 concurrent.futures 모듈 및 구체적인 하위 클래스 Executor, 우리는 쉽게 스레드 풀을 만들 수 있습니다. 이를 위해 우리는ThreadPoolExecutor풀에서 원하는 스레드 수로 기본적으로 숫자는 5입니다. 그러면 스레드 풀에 작업을 제출할 수 있습니다. 언제 우리가submit() 작업, 우리는 다시 Future. Future 객체에는done(), 미래가 해결되었는지 알려줍니다. 이를 통해 특정 미래 개체에 대한 값이 설정되었습니다. 작업이 완료되면 스레드 풀 실행기가 값을 미래 개체로 설정합니다.
예
from concurrent.futures import ThreadPoolExecutor
from time import sleep
def task(message):
sleep(2)
return message
def main():
executor = ThreadPoolExecutor(5)
future = executor.submit(task, ("Completed"))
print(future.done())
sleep(2)
print(future.done())
print(future.result())
if __name__ == '__main__':
main()
산출
False
True
Completed
위의 예에서 ThreadPoolExecutor5 개의 스레드로 구성되었습니다. 그런 다음 메시지를 제공하기 전에 2 초 동안 대기하는 작업이 스레드 풀 실행기에 제출됩니다. 출력에서 볼 수 있듯이 작업은 2 초까지 완료되지 않으므로 첫 번째 호출은done()False를 반환합니다. 2 초 후에 작업이 완료되고 다음을 호출하여 미래의 결과를 얻습니다.result() 그것에 방법.
ThreadPoolExecutor 인스턴스화 – 컨텍스트 관리자
인스턴스화하는 또 다른 방법 ThreadPoolExecutor컨텍스트 관리자의 도움을받습니다. 위의 예에서 사용한 방법과 유사하게 작동합니다. 컨텍스트 관리자를 사용하는 가장 큰 장점은 구문 적으로 좋아 보인다는 것입니다. 인스턴스화는 다음 코드를 사용하여 수행 할 수 있습니다.
with ThreadPoolExecutor(max_workers = 5) as executor
예
다음 예제는 Python 문서에서 차용 한 것입니다. 이 예에서는 우선concurrent.futures모듈을 가져와야합니다. 그런 다음 이름이 지정된 함수load_url()요청 된 URL을로드 할 생성됩니다. 그런 다음 함수는ThreadPoolExecutor풀에 5 개의 스레드가 있습니다. 그만큼ThreadPoolExecutor컨텍스트 관리자로 활용되었습니다. 우리는 다음을 호출하여 미래의 결과를 얻을 수 있습니다.result() 그것에 방법.
import concurrent.futures
import urllib.request
URLS = ['http://www.foxnews.com/',
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/',
'http://some-made-up-domain.com/']
def load_url(url, timeout):
with urllib.request.urlopen(url, timeout = timeout) as conn:
return conn.read()
with concurrent.futures.ThreadPoolExecutor(max_workers = 5) as executor:
future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
for future in concurrent.futures.as_completed(future_to_url):
url = future_to_url[future]
try:
data = future.result()
except Exception as exc:
print('%r generated an exception: %s' % (url, exc))
else:
print('%r page is %d bytes' % (url, len(data)))
산출
다음은 위의 Python 스크립트의 출력입니다.
'http://some-made-up-domain.com/' generated an exception: <urlopen error [Errno 11004] getaddrinfo failed>
'http://www.foxnews.com/' page is 229313 bytes
'http://www.cnn.com/' page is 168933 bytes
'http://www.bbc.co.uk/' page is 283893 bytes
'http://europe.wsj.com/' page is 938109 bytes
Executor.map () 함수 사용
파이썬 map()기능은 많은 작업에서 널리 사용됩니다. 이러한 작업 중 하나는 iterables 내의 모든 요소에 특정 함수를 적용하는 것입니다. 마찬가지로 반복기의 모든 요소를 함수에 매핑하고이를 독립적 인 작업으로 제출할 수 있습니다.ThreadPoolExecutor. 함수의 작동 방식을 이해하려면 다음 Python 스크립트 예제를 고려하십시오.
예
아래이 예에서는 map 함수를 사용하여 square() 값 배열의 모든 값에 대한 함수입니다.
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import as_completed
values = [2,3,4,5]
def square(n):
return n * n
def main():
with ThreadPoolExecutor(max_workers = 3) as executor:
results = executor.map(square, values)
for result in results:
print(result)
if __name__ == '__main__':
main()
산출
위의 Python 스크립트는 다음 출력을 생성합니다.
4
9
16
25
프로세스 풀은 스레드 풀을 만들고 사용한 것과 동일한 방식으로 만들고 사용할 수 있습니다. 프로세스 풀은 작업을받을 준비가되어있는 사전 인스턴스화 및 유휴 프로세스 그룹으로 정의 할 수 있습니다. 많은 작업을 수행해야 할 때 모든 작업에 대해 새 프로세스를 인스턴스화하는 것보다 프로세스 풀을 만드는 것이 좋습니다.
Python 모듈 – Concurrent.futures
Python 표준 라이브러리에는 concurrent.futures. 이 모듈은 개발자에게 비동기 작업을 시작하기위한 고급 인터페이스를 제공하기 위해 Python 3.2에 추가되었습니다. 스레드 또는 프로세스 풀을 사용하여 작업을 실행하기위한 인터페이스를 제공하기위한 Python의 스레딩 및 다중 처리 모듈 상단에있는 추상화 계층입니다.
다음 섹션에서는 concurrent.futures 모듈의 여러 하위 클래스를 살펴 보겠습니다.
Executor 클래스
Executor 의 추상 클래스입니다 concurrent.futuresPython 모듈. 직접 사용할 수 없으며 다음 구체적인 하위 클래스 중 하나를 사용해야합니다.
- ThreadPoolExecutor
- ProcessPoolExecutor
ProcessPoolExecutor – 구체적인 하위 클래스
Executor 클래스의 구체적인 하위 클래스 중 하나입니다. 다중 처리를 사용하고 작업 제출을위한 프로세스 풀을 얻습니다. 이 풀은 사용 가능한 프로세스에 작업을 할당하고 실행하도록 예약합니다.
ProcessPoolExecutor를 만드는 방법은 무엇입니까?
의 도움으로 concurrent.futures 모듈 및 구체적인 하위 클래스 Executor, 우리는 쉽게 프로세스 풀을 만들 수 있습니다. 이를 위해 우리는ProcessPoolExecutor풀에서 원하는 프로세스 수로 기본적으로 숫자는 5입니다. 그 다음에는 프로세스 풀에 작업을 제출합니다.
예
이제 스레드 풀을 만들 때 사용한 것과 동일한 예를 고려할 것입니다. 유일한 차이점은 ProcessPoolExecutor 대신에 ThreadPoolExecutor .
from concurrent.futures import ProcessPoolExecutor
from time import sleep
def task(message):
sleep(2)
return message
def main():
executor = ProcessPoolExecutor(5)
future = executor.submit(task, ("Completed"))
print(future.done())
sleep(2)
print(future.done())
print(future.result())
if __name__ == '__main__':
main()
산출
False
False
Completed
위의 예에서 프로세스PoolExecutor5 개의 스레드로 구성되었습니다. 그런 다음 메시지를 제공하기 전에 2 초 동안 대기하는 작업이 프로세스 풀 실행기에 제출됩니다. 출력에서 볼 수 있듯이 작업은 2 초까지 완료되지 않으므로 첫 번째 호출은done()False를 반환합니다. 2 초 후에 작업이 완료되고 다음을 호출하여 미래의 결과를 얻습니다.result() 그것에 방법.
ProcessPoolExecutor 인스턴스화 – 컨텍스트 관리자
ProcessPoolExecutor를 인스턴스화하는 또 다른 방법은 컨텍스트 관리자를 사용하는 것입니다. 위의 예에서 사용한 방법과 유사하게 작동합니다. 컨텍스트 관리자를 사용하는 가장 큰 장점은 구문 적으로 좋아 보인다는 것입니다. 인스턴스화는 다음 코드를 사용하여 수행 할 수 있습니다.
with ProcessPoolExecutor(max_workers = 5) as executor
예
더 나은 이해를 위해 스레드 풀을 생성 할 때 사용한 것과 동일한 예제를 사용합니다. 이 예에서는 먼저concurrent.futures기준 치수. 그런 다음 이름이 지정된 함수load_url()요청 된 URL을로드 할 생성됩니다. 그만큼ProcessPoolExecutor그런 다음 풀에있는 5 개의 스레드로 생성됩니다. 과정PoolExecutor컨텍스트 관리자로 활용되었습니다. 우리는 다음을 호출하여 미래의 결과를 얻을 수 있습니다.result() 그것에 방법.
import concurrent.futures
from concurrent.futures import ProcessPoolExecutor
import urllib.request
URLS = ['http://www.foxnews.com/',
'http://www.cnn.com/',
'http://europe.wsj.com/',
'http://www.bbc.co.uk/',
'http://some-made-up-domain.com/']
def load_url(url, timeout):
with urllib.request.urlopen(url, timeout = timeout) as conn:
return conn.read()
def main():
with concurrent.futures.ProcessPoolExecutor(max_workers=5) as executor:
future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
for future in concurrent.futures.as_completed(future_to_url):
url = future_to_url[future]
try:
data = future.result()
except Exception as exc:
print('%r generated an exception: %s' % (url, exc))
else:
print('%r page is %d bytes' % (url, len(data)))
if __name__ == '__main__':
main()
산출
위의 Python 스크립트는 다음 출력을 생성합니다.
'http://some-made-up-domain.com/' generated an exception: <urlopen error [Errno 11004] getaddrinfo failed>
'http://www.foxnews.com/' page is 229476 bytes
'http://www.cnn.com/' page is 165323 bytes
'http://www.bbc.co.uk/' page is 284981 bytes
'http://europe.wsj.com/' page is 967575 bytes
Executor.map () 함수 사용
파이썬 map()기능은 많은 작업을 수행하는 데 널리 사용됩니다. 이러한 작업 중 하나는 iterables 내의 모든 요소에 특정 함수를 적용하는 것입니다. 마찬가지로 반복기의 모든 요소를 함수에 매핑하고이를 독립 작업으로 제출할 수 있습니다.ProcessPoolExecutor. 이를 이해하려면 다음 Python 스크립트 예제를 고려하십시오.
예
다음을 사용하여 스레드 풀을 만들 때 사용한 것과 동일한 예를 고려합니다. Executor.map()함수. 아래 주어진 예에서는 map 함수를 사용하여square() 값 배열의 모든 값에 대한 함수입니다.
from concurrent.futures import ProcessPoolExecutor
from concurrent.futures import as_completed
values = [2,3,4,5]
def square(n):
return n * n
def main():
with ProcessPoolExecutor(max_workers = 3) as executor:
results = executor.map(square, values)
for result in results:
print(result)
if __name__ == '__main__':
main()
산출
위의 Python 스크립트는 다음 출력을 생성합니다.
4
9
16
25
ProcessPoolExecutor 및 ThreadPoolExecutor는 언제 사용합니까?
이제 Executor 클래스 (ThreadPoolExecutor 및 ProcessPoolExecutor) 모두에 대해 공부 했으므로 언제 어떤 실행기를 사용할지 알아야합니다. CPU 바운드 워크로드의 경우 ProcessPoolExecutor를 선택하고 I / O 바운드 워크로드의 경우 ThreadPoolExecutor를 선택해야합니다.
우리가 사용한다면 ProcessPoolExecutor, 그러면 다중 처리를 사용하기 때문에 GIL에 대해 걱정할 필요가 없습니다. 또한 실행 시간은ThreadPoolExecution. 이를 이해하려면 다음 Python 스크립트 예제를 고려하십시오.
예
import time
import concurrent.futures
value = [8000000, 7000000]
def counting(n):
start = time.time()
while n > 0:
n -= 1
return time.time() - start
def main():
start = time.time()
with concurrent.futures.ProcessPoolExecutor() as executor:
for number, time_taken in zip(value, executor.map(counting, value)):
print('Start: {} Time taken: {}'.format(number, time_taken))
print('Total time taken: {}'.format(time.time() - start))
if __name__ == '__main__':
main()
산출
Start: 8000000 Time taken: 1.5509998798370361
Start: 7000000 Time taken: 1.3259999752044678
Total time taken: 2.0840001106262207
Example- Python script with ThreadPoolExecutor:
import time
import concurrent.futures
value = [8000000, 7000000]
def counting(n):
start = time.time()
while n > 0:
n -= 1
return time.time() - start
def main():
start = time.time()
with concurrent.futures.ThreadPoolExecutor() as executor:
for number, time_taken in zip(value, executor.map(counting, value)):
print('Start: {} Time taken: {}'.format(number, time_taken))
print('Total time taken: {}'.format(time.time() - start))
if __name__ == '__main__':
main()
산출
Start: 8000000 Time taken: 3.8420000076293945
Start: 7000000 Time taken: 3.6010000705718994
Total time taken: 3.8480000495910645
위의 두 프로그램의 출력에서 우리는 사용하는 동안 실행 시간의 차이를 볼 수 있습니다. ProcessPoolExecutor 과 ThreadPoolExecutor.
이 장에서는 멀티 프로세싱과 멀티 스레딩 간의 비교에 더 중점을 둘 것입니다.
다중 처리
단일 컴퓨터 시스템 내에서 두 개 이상의 CPU 장치를 사용하는 것입니다. 컴퓨터 시스템에서 사용 가능한 전체 CPU 코어를 활용하여 하드웨어의 잠재력을 최대한 활용하는 가장 좋은 방법입니다.
멀티 스레딩
여러 스레드를 동시에 실행하여 운영 체제 사용을 관리하는 CPU의 기능입니다. 멀티 스레딩의 주요 개념은 프로세스를 여러 스레드로 분할하여 병렬화를 달성하는 것입니다.
다음 표는 그들 사이의 중요한 차이점 중 일부를 보여줍니다.
다중 처리 | 다중 프로그래밍 |
---|---|
다중 처리는 여러 CPU에서 여러 프로세스를 동시에 처리하는 것을 말합니다. | 멀티 프로그래밍은 여러 프로그램을 주 메모리에 동시에 유지하고 단일 CPU를 사용하여 동시에 실행합니다. |
여러 CPU를 사용합니다. | 단일 CPU를 사용합니다. |
병렬 처리를 허용합니다. | 컨텍스트 전환이 발생합니다. |
작업을 처리하는 데 걸리는 시간이 줄어 듭니다. | 작업을 처리하는 데 더 많은 시간이 걸립니다. |
컴퓨터 시스템의 장치를 훨씬 효율적으로 활용할 수 있습니다. | 다중 처리보다 덜 효율적입니다. |
일반적으로 더 비쌉니다. | 이러한 시스템은 저렴합니다. |
글로벌 인터프리터 잠금 (GIL)의 영향 제거
동시 애플리케이션으로 작업하는 동안 Python에는 다음과 같은 제한이 있습니다. GIL (Global Interpreter Lock). GIL은 결코 다중 CPU 코어를 사용하도록 허용하지 않으므로 Python에는 진정한 스레드가 없다고 말할 수 있습니다. GIL은 뮤텍스입니다. 상호 배제 잠금으로 스레드를 안전하게 만듭니다. 즉, GIL이 여러 스레드가 Python 코드를 병렬로 실행하는 것을 방지한다고 말할 수 있습니다. 잠금은 한 번에 하나의 스레드 만 보유 할 수 있으며 스레드를 실행하려면 먼저 잠금을 획득해야합니다.
다중 처리를 사용하면 GIL로 인한 제한을 효과적으로 우회 할 수 있습니다.
다중 처리를 사용함으로써 우리는 여러 프로세스의 기능을 활용하고 있으므로 GIL의 여러 인스턴스를 활용하고 있습니다.
이로 인해 프로그램 내에서 한 번에 한 스레드의 바이트 코드를 실행하는 데 제한이 없습니다.
Python에서 프로세스 시작
다음 세 가지 방법을 사용하여 다중 처리 모듈 내에서 Python에서 프로세스를 시작할 수 있습니다.
- Fork
- Spawn
- Forkserver
Fork로 프로세스 생성
Fork 명령은 UNIX에서 볼 수있는 표준 명령입니다. 자식 프로세스라는 새 프로세스를 만드는 데 사용됩니다. 이 자식 프로세스는 부모 프로세스라는 프로세스와 동시에 실행됩니다. 이러한 자식 프로세스는 부모 프로세스와 동일하며 부모가 사용할 수있는 모든 리소스를 상속합니다. 다음 시스템 호출은 Fork로 프로세스를 만드는 동안 사용됩니다-
fork()− 일반적으로 커널에서 구현되는 시스템 호출입니다. 프로세스의 복사본을 만드는 데 사용됩니다 .p>
getpid() −이 시스템 호출은 호출 프로세스의 프로세스 ID (PID)를 반환합니다.
예
다음 Python 스크립트 예제는 새 자식 프로세스를 만들고 자식 및 부모 프로세스의 PID를 얻는 방법을 이해하는 데 도움이됩니다.
import os
def child():
n = os.fork()
if n > 0:
print("PID of Parent process is : ", os.getpid())
else:
print("PID of Child process is : ", os.getpid())
child()
산출
PID of Parent process is : 25989
PID of Child process is : 25990
Spawn으로 프로세스 생성
Spawn은 새로운 것을 시작하는 것을 의미합니다. 따라서 프로세스 생성은 부모 프로세스가 새 프로세스를 생성하는 것을 의미합니다. 부모 프로세스는 비동기 적으로 실행을 계속하거나 자식 프로세스가 실행을 끝낼 때까지 기다립니다. 프로세스를 생성하려면 다음 단계를 따르십시오.
다중 처리 모듈을 가져 오는 중입니다.
개체 프로세스 만들기.
호출하여 프로세스 활동 시작 start() 방법.
프로세스가 작업을 마칠 때까지 기다렸다가 호출하여 종료합니다. join() 방법.
예
다음 Python 스크립트 예제는 세 가지 프로세스를 생성하는 데 도움이됩니다.
import multiprocessing
def spawn_process(i):
print ('This is process: %s' %i)
return
if __name__ == '__main__':
Process_jobs = []
for i in range(3):
p = multiprocessing.Process(target = spawn_process, args = (i,))
Process_jobs.append(p)
p.start()
p.join()
산출
This is process: 0
This is process: 1
This is process: 2
Forkserver로 프로세스 생성
Forkserver 메커니즘은 Unix 파이프를 통한 파일 설명자 전달을 지원하는 선택된 UNIX 플랫폼에서만 사용할 수 있습니다. Forkserver 메커니즘의 작동을 이해하려면 다음 사항을 고려하십시오.
새 프로세스를 시작하기 위해 Forkserver 메커니즘을 사용하여 서버가 인스턴스화됩니다.
그런 다음 서버는 명령을 수신하고 새 프로세스를 만들기위한 모든 요청을 처리합니다.
새로운 프로세스를 생성하기 위해 Python 프로그램은 Forkserver에 요청을 보내고 프로세스를 생성합니다.
마침내 우리는 프로그램에서이 새로 생성 된 프로세스를 사용할 수 있습니다.
Python의 데몬 프로세스
파이썬 multiprocessing모듈은 데몬 옵션을 통해 데몬 프로세스를 가질 수 있습니다. 데몬 프로세스 또는 백그라운드에서 실행중인 프로세스는 데몬 스레드와 유사한 개념을 따릅니다. 백그라운드에서 프로세스를 실행하려면 데몬 플래그를 true로 설정해야합니다. 데몬 프로세스는 주 프로세스가 실행되는 동안 계속 실행되며 실행이 완료되거나 주 프로그램이 종료 될 때 종료됩니다.
예
여기서는 데몬 스레드에서 사용 된 것과 동일한 예제를 사용합니다. 유일한 차이점은 모듈이multithreading ...에 multiprocessing데몬 플래그를 true로 설정합니다. 그러나 아래와 같이 출력이 변경됩니다.
import multiprocessing
import time
def nondaemonProcess():
print("starting my Process")
time.sleep(8)
print("ending my Process")
def daemonProcess():
while True:
print("Hello")
time.sleep(2)
if __name__ == '__main__':
nondaemonProcess = multiprocessing.Process(target = nondaemonProcess)
daemonProcess = multiprocessing.Process(target = daemonProcess)
daemonProcess.daemon = True
nondaemonProcess.daemon = False
daemonProcess.start()
nondaemonProcess.start()
산출
starting my Process
ending my Process
데몬 스레드가 생성 한 것과 비교할 때 출력은 데몬이없는 모드의 프로세스에 출력이 있기 때문에 다릅니다. 따라서 데몬 프로세스는 실행중인 프로세스의 지속성을 피하기 위해 기본 프로그램이 종료 된 후 자동으로 종료됩니다.
Python에서 프로세스 종료
다음을 사용하여 프로세스를 즉시 종료하거나 종료 할 수 있습니다. terminate()방법. 이 메서드를 사용하여 함수의 도움으로 생성 된 자식 프로세스를 실행을 완료하기 직전에 종료합니다.
예
import multiprocessing
import time
def Child_process():
print ('Starting function')
time.sleep(5)
print ('Finished function')
P = multiprocessing.Process(target = Child_process)
P.start()
print("My Process has terminated, terminating main thread")
print("Terminating Child Process")
P.terminate()
print("Child Process successfully terminated")
산출
My Process has terminated, terminating main thread
Terminating Child Process
Child Process successfully terminated
출력은 Child_process () 함수의 도움으로 생성 된 자식 프로세스를 실행하기 전에 프로그램이 종료됨을 보여줍니다. 이는 자식 프로세스가 성공적으로 종료되었음을 의미합니다.
Python에서 현재 프로세스 식별
운영 체제의 모든 프로세스에는 PID라는 프로세스 ID가 있습니다. Python에서는 다음 명령을 사용하여 현재 프로세스의 PID를 찾을 수 있습니다.
import multiprocessing
print(multiprocessing.current_process().pid)
예
다음 Python 스크립트 예제는 주 프로세스의 PID와 자식 프로세스의 PID를 찾는 데 도움이됩니다.
import multiprocessing
import time
def Child_process():
print("PID of Child Process is: {}".format(multiprocessing.current_process().pid))
print("PID of Main process is: {}".format(multiprocessing.current_process().pid))
P = multiprocessing.Process(target=Child_process)
P.start()
P.join()
산출
PID of Main process is: 9401
PID of Child Process is: 9402
서브 클래스에서 프로세스 사용
스레드를 만들 수 있습니다. threading.Thread수업. 또한, 우리는 하위 분류를 통해 프로세스를 생성 할 수도 있습니다.multiprocessing.Process수업. 서브 클래스에서 프로세스를 사용하려면 다음 사항을 고려해야합니다.
새로운 하위 클래스를 정의해야합니다. Process 수업.
우리는 재정의해야합니다 _init_(self [,args] ) 수업.
우리는 run(self [,args] ) 무엇을 구현하는 방법 Process
프로세스를 시작해야합니다.start() 방법.
예
import multiprocessing
class MyProcess(multiprocessing.Process):
def run(self):
print ('called run method in process: %s' %self.name)
return
if __name__ == '__main__':
jobs = []
for i in range(5):
P = MyProcess()
jobs.append(P)
P.start()
P.join()
산출
called run method in process: MyProcess-1
called run method in process: MyProcess-2
called run method in process: MyProcess-3
called run method in process: MyProcess-4
called run method in process: MyProcess-5
Python 다중 처리 모듈 – 풀 클래스
단순 병렬에 대해 이야기하면 processingPython 애플리케이션에서 작업을 수행 한 다음 다중 처리 모듈은 Pool 클래스를 제공합니다. 다음 방법Pool 클래스는 메인 프로그램 내에서 자식 프로세스의 수를 늘리는 데 사용할 수 있습니다.
apply () 메서드
이 방법은.submit()의 방법 .ThreadPoolExecutor.결과가 준비 될 때까지 차단됩니다.
apply_async () 메서드
작업의 병렬 실행이 필요할 때apply_async()풀에 작업을 제출하는 방법. 모든 자식 프로세스가 실행될 때까지 주 스레드를 잠그지 않는 비동기 작업입니다.
map () 메서드
처럼 apply()결과가 준비 될 때까지 차단됩니다. 내장과 동일map() 반복 가능한 데이터를 여러 청크로 분할하고 별도의 작업으로 프로세스 풀에 제출하는 함수입니다.
map_async () 메서드
그것은의 변형입니다 map() 방법으로 apply_async() ~에 apply()방법. 결과 개체를 반환합니다. 결과가 준비되면 콜 러블이 적용됩니다. 콜 러블은 즉시 완료되어야합니다. 그렇지 않으면 결과를 처리하는 스레드가 차단됩니다.
예
다음 예제는 병렬 실행을 수행하기위한 프로세스 풀을 구현하는 데 도움이됩니다. 숫자의 제곱에 대한 간단한 계산은square() 기능을 통해 multiprocessing.Pool방법. 그때pool.map() 입력은 0에서 4까지의 정수 목록이기 때문에 5를 제출하는 데 사용되었습니다. 결과는 다음 위치에 저장됩니다. p_outputs 인쇄됩니다.
def square(n):
result = n*n
return result
if __name__ == '__main__':
inputs = list(range(5))
p = multiprocessing.Pool(processes = 4)
p_outputs = pool.map(function_square, inputs)
p.close()
p.join()
print ('Pool :', p_outputs)
산출
Pool : [0, 1, 4, 9, 16]
프로세스 상호 통신은 프로세스 간의 데이터 교환을 의미합니다. 병렬 애플리케이션 개발을 위해서는 프로세스 간 데이터 교환이 필요합니다. 다음 다이어그램은 여러 하위 프로세스 간의 동기화를위한 다양한 통신 메커니즘을 보여줍니다.
다양한 통신 메커니즘
이 섹션에서는 다양한 통신 메커니즘에 대해 알아 봅니다. 메커니즘은 아래에 설명되어 있습니다.
대기열
큐는 다중 프로세스 프로그램과 함께 사용할 수 있습니다. 큐 클래스multiprocessing 모듈은 Queue.Queue수업. 따라서 동일한 API를 사용할 수 있습니다.Multiprocessing.Queue는 프로세스 간의 통신을위한 스레드 및 프로세스 안전 FIFO (선입 선출) 메커니즘을 제공합니다.
예
다음은 다중 처리의 Queue 클래스 개념을 이해하기 위해 다중 처리에 대한 Python 공식 문서에서 가져온 간단한 예제입니다.
from multiprocessing import Process, Queue
import queue
import random
def f(q):
q.put([42, None, 'hello'])
def main():
q = Queue()
p = Process(target = f, args = (q,))
p.start()
print (q.get())
if __name__ == '__main__':
main()
산출
[42, None, 'hello']
파이프
다중 프로세스 프로그램에서 프로세스 간 통신에 사용되는 데이터 구조입니다. Pipe () 함수는 기본적으로 양방향 (양방향) 인 파이프로 연결된 연결 객체 쌍을 반환합니다. 그것은 다음과 같은 방식으로 작동합니다-
파이프의 두 끝을 나타내는 연결 개체 쌍을 반환합니다.
모든 개체에는 두 가지 방법이 있습니다. send() 과 recv(), 프로세스간에 통신합니다.
예
다음은 다중 처리에 대한 파이썬 공식 문서에서 가져온 간단한 예제입니다. Pipe() 다중 처리의 기능.
from multiprocessing import Process, Pipe
def f(conn):
conn.send([42, None, 'hello'])
conn.close()
if __name__ == '__main__':
parent_conn, child_conn = Pipe()
p = Process(target = f, args = (child_conn,))
p.start()
print (parent_conn.recv())
p.join()
산출
[42, None, 'hello']
매니저
관리자는 모든 사용자간에 공유 된 정보를 조정하는 방법을 제공하는 다중 처리 모듈 클래스입니다. 관리자 개체는 공유 개체를 관리하고 다른 프로세스가이를 조작 할 수 있도록하는 서버 프로세스를 제어합니다. 즉, 관리자는 서로 다른 프로세스간에 공유 할 수있는 데이터를 생성하는 방법을 제공합니다. 다음은 관리자 개체의 다른 속성입니다-
관리자의 주된 속성은 공유 객체를 관리하는 서버 프로세스를 제어하는 것입니다.
또 다른 중요한 속성은 프로세스가 수정할 때 모든 공유 객체를 업데이트하는 것입니다.
예
다음은 서버 프로세스에서 목록 레코드를 만든 다음 해당 목록에 새 레코드를 추가하기 위해 관리자 개체를 사용하는 예입니다.
import multiprocessing
def print_records(records):
for record in records:
print("Name: {0}\nScore: {1}\n".format(record[0], record[1]))
def insert_record(record, records):
records.append(record)
print("A New record is added\n")
if __name__ == '__main__':
with multiprocessing.Manager() as manager:
records = manager.list([('Computers', 1), ('Histoty', 5), ('Hindi',9)])
new_record = ('English', 3)
p1 = multiprocessing.Process(target = insert_record, args = (new_record, records))
p2 = multiprocessing.Process(target = print_records, args = (records,))
p1.start()
p1.join()
p2.start()
p2.join()
산출
A New record is added
Name: Computers
Score: 1
Name: Histoty
Score: 5
Name: Hindi
Score: 9
Name: English
Score: 3
Manager의 네임 스페이스 개념
관리자 클래스는 여러 프로세스에서 여러 속성을 공유하는 빠른 방법 인 네임 스페이스 개념과 함께 제공됩니다. 네임 스페이스에는 호출 할 수있는 공용 메서드가 없지만 쓰기 가능한 속성이 있습니다.
예
다음 Python 스크립트 예제는 메인 프로세스와 하위 프로세스에서 데이터를 공유하기 위해 네임 스페이스를 활용하는 데 도움이됩니다.
import multiprocessing
def Mng_NaSp(using_ns):
using_ns.x +=5
using_ns.y *= 10
if __name__ == '__main__':
manager = multiprocessing.Manager()
using_ns = manager.Namespace()
using_ns.x = 1
using_ns.y = 1
print ('before', using_ns)
p = multiprocessing.Process(target = Mng_NaSp, args = (using_ns,))
p.start()
p.join()
print ('after', using_ns)
산출
before Namespace(x = 1, y = 1)
after Namespace(x = 6, y = 10)
Ctypes 배열 및 값
Multiprocessing 모듈은 공유 메모리 맵에 데이터를 저장하기위한 Array 및 Value 객체를 제공합니다. Array 공유 메모리에서 할당 된 ctypes 배열이며 Value 공유 메모리에서 할당 된 ctypes 객체입니다.
함께하려면 다중 처리에서 Process, Value, Array를 가져옵니다.
예
다음 Python 스크립트는 프로세스간에 일부 데이터를 공유하기 위해 Ctypes Array 및 Value를 활용하기 위해 Python 문서에서 가져온 예제입니다.
def f(n, a):
n.value = 3.1415927
for i in range(len(a)):
a[i] = -a[i]
if __name__ == '__main__':
num = Value('d', 0.0)
arr = Array('i', range(10))
p = Process(target = f, args = (num, arr))
p.start()
p.join()
print (num.value)
print (arr[:])
산출
3.1415927
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
순차적 프로세스 통신 (CSP)
CSP는 동시 모델을 특징으로하는 다른 시스템과 시스템의 상호 작용을 설명하는 데 사용됩니다. CSP는 동시성 또는 메시지 전달을 통한 프로그램 작성을위한 프레임 워크이므로 동시성을 설명하는 데 효과적입니다.
Python 라이브러리 – PyCSP
CSP에있는 핵심 기본 요소를 구현하기 위해 Python에는 PyCSP라는 라이브러리가 있습니다. 매우 쉽게 이해할 수 있도록 구현을 매우 짧고 읽기 쉽게 유지합니다. 다음은 PyCSP의 기본 프로세스 네트워크입니다.
위의 PyCSP 프로세스 네트워크에는 Process1과 Process 2의 두 가지 프로세스가 있습니다. 이러한 프로세스는 채널 1과 채널 2의 두 채널을 통해 메시지를 전달하여 통신합니다.
PyCSP 설치
다음 명령을 사용하여 Python 라이브러리 PyCSP를 설치할 수 있습니다.
pip install PyCSP
예
다음 Python 스크립트는 두 프로세스를 서로 병렬로 실행하는 간단한 예제입니다. 그것은 PyCSP python libabary의 도움으로 이루어집니다.
from pycsp.parallel import *
import time
@process
def P1():
time.sleep(1)
print('P1 exiting')
@process
def P2():
time.sleep(1)
print('P2 exiting')
def main():
Parallel(P1(), P2())
print('Terminating')
if __name__ == '__main__':
main()
위의 스크립트에서 두 가지 기능 즉 P1 과 P2 만들어지고 장식되었습니다 @process 프로세스로 변환합니다.
산출
P2 exiting
P1 exiting
Terminating
이벤트 중심 프로그래밍은 이벤트에 중점을 둡니다. 결국 프로그램의 흐름은 이벤트에 따라 달라집니다. 지금까지 우리는 순차 또는 병렬 실행 모델을 다루었지만 이벤트 중심 프로그래밍의 개념을 가진 모델을 비동기 모델이라고합니다. 이벤트 기반 프로그래밍은 항상 새로 들어오는 이벤트를 수신하는 이벤트 루프에 의존합니다. 이벤트 중심 프로그래밍의 작동은 이벤트에 따라 다릅니다. 이벤트가 반복되면 이벤트는 실행할 항목과 순서를 결정합니다. 다음 순서도는 이것이 어떻게 작동하는지 이해하는 데 도움이 될 것입니다.
Python 모듈 – Asyncio
Asyncio 모듈은 Python 3.4에 추가되었으며 코 루틴을 사용하여 단일 스레드 동시 코드를 작성하기위한 인프라를 제공합니다. 다음은 Asyncio 모듈에서 사용하는 다른 개념입니다.
이벤트 루프
이벤트 루프는 계산 코드의 모든 이벤트를 처리하는 기능입니다. 전체 프로그램을 실행하는 동안 내내 작동하며 이벤트의 수신 및 실행을 추적합니다. Asyncio 모듈은 프로세스 당 단일 이벤트 루프를 허용합니다. 다음은 이벤트 루프를 관리하기 위해 Asyncio 모듈에서 제공하는 몇 가지 방법입니다.
loop = get_event_loop() −이 방법은 현재 컨텍스트에 대한 이벤트 루프를 제공합니다.
loop.call_later(time_delay,callback,argument) −이 메소드는 주어진 time_delay 초 후에 호출 될 콜백을 정렬합니다.
loop.call_soon(callback,argument)−이 메서드는 가능한 한 빨리 호출 할 콜백을 준비합니다. 콜백은 call_soon ()이 반환 된 후 컨트롤이 이벤트 루프로 반환 될 때 호출됩니다.
loop.time() −이 방법은 이벤트 루프의 내부 시계에 따라 현재 시간을 반환하는 데 사용됩니다.
asyncio.set_event_loop() −이 방법은 현재 컨텍스트에 대한 이벤트 루프를 루프로 설정합니다.
asyncio.new_event_loop() −이 메서드는 새로운 이벤트 루프 객체를 생성하고 반환합니다.
loop.run_forever() −이 메서드는 stop () 메서드가 호출 될 때까지 실행됩니다.
예
다음 이벤트 루프 예제는 인쇄에 도움이됩니다. hello worldget_event_loop () 메서드를 사용합니다. 이 예제는 Python 공식 문서에서 가져온 것입니다.
import asyncio
def hello_world(loop):
print('Hello World')
loop.stop()
loop = asyncio.get_event_loop()
loop.call_soon(hello_world, loop)
loop.run_forever()
loop.close()
산출
Hello World
선물
이는 아직 수행되지 않은 계산을 나타내는 concurrent.futures.Future 클래스와 호환됩니다. asyncio.futures.Future와 concurrent.futures.Future 사이에는 다음과 같은 차이점이 있습니다.
result () 및 exception () 메서드는 시간 초과 인수를받지 않고 미래가 아직 완료되지 않은 경우 예외를 발생시킵니다.
add_done_callback ()에 등록 된 콜백은 항상 이벤트 루프의 call_soon ()을 통해 호출됩니다.
asyncio.futures.Future 클래스는 concurrent.futures 패키지의 wait () 및 as_completed () 함수와 호환되지 않습니다.
예
다음은 asyncio.futures.future 클래스 사용 방법을 이해하는 데 도움이되는 예제입니다.
import asyncio
async def Myoperation(future):
await asyncio.sleep(2)
future.set_result('Future Completed')
loop = asyncio.get_event_loop()
future = asyncio.Future()
asyncio.ensure_future(Myoperation(future))
try:
loop.run_until_complete(future)
print(future.result())
finally:
loop.close()
산출
Future Completed
코 루틴
Asyncio의 코 루틴 개념은 스레딩 모듈 아래의 표준 Thread 개체 개념과 유사합니다. 이것은 서브 루틴 개념의 일반화입니다. 코 루틴은 실행 중에 일시 중단 될 수 있으므로 외부 처리를 기다렸다가 외부 처리가 완료되었을 때 중지 된 지점에서 반환됩니다. 다음 두 가지 방법은 코 루틴을 구현하는 데 도움이됩니다.
비동기 def function ()
Asyncio 모듈에서 코 루틴을 구현하는 방법입니다. 다음은 동일한 Python 스크립트입니다.
import asyncio
async def Myoperation():
print("First Coroutine")
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(Myoperation())
finally:
loop.close()
산출
First Coroutine
@ asyncio.coroutine 데코레이터
코 루틴을 구현하는 또 다른 방법은 @ asyncio.coroutine 데코레이터로 생성기를 활용하는 것입니다. 다음은 동일한 Python 스크립트입니다.
import asyncio
@asyncio.coroutine
def Myoperation():
print("First Coroutine")
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(Myoperation())
finally:
loop.close()
산출
First Coroutine
과제
Asyncio 모듈의이 하위 클래스는 이벤트 루프 내에서 병렬 방식으로 코 루틴을 실행합니다. 다음 Python 스크립트는 일부 작업을 병렬로 처리하는 예입니다.
import asyncio
import time
async def Task_ex(n):
time.sleep(1)
print("Processing {}".format(n))
async def Generator_task():
for i in range(10):
asyncio.ensure_future(Task_ex(i))
int("Tasks Completed")
asyncio.sleep(2)
loop = asyncio.get_event_loop()
loop.run_until_complete(Generator_task())
loop.close()
산출
Tasks Completed
Processing 0
Processing 1
Processing 2
Processing 3
Processing 4
Processing 5
Processing 6
Processing 7
Processing 8
Processing 9
수송
Asyncio 모듈은 다양한 유형의 통신을 구현하기위한 전송 클래스를 제공합니다. 이러한 클래스는 스레드로부터 안전하지 않으며 통신 채널을 설정 한 후 항상 프로토콜 인스턴스와 쌍을 이룹니다.
다음은 BaseTransport에서 상속 된 고유 한 전송 유형입니다.
ReadTransport − 이것은 읽기 전용 전송을위한 인터페이스입니다.
WriteTransport − 이것은 쓰기 전용 전송을위한 인터페이스입니다.
DatagramTransport − 데이터 전송을위한 인터페이스입니다.
BaseSubprocessTransport − BaseTransport 클래스와 유사합니다.
다음은 네 가지 전송 유형에 걸쳐 일시적인 BaseTransport 클래스의 다섯 가지 고유 한 방법입니다.
close() − 트랜스 포트를 닫습니다.
is_closing() −이 메서드는 트랜스 포트가 닫혀 있거나 이미 닫혀 있으면 참을 반환합니다.
get_extra_info(name, default = none) − 이는 운송에 대한 추가 정보를 제공합니다.
get_protocol() −이 방법은 현재 프로토콜을 반환합니다.
프로토콜
Asyncio 모듈은 네트워크 프로토콜을 구현하기 위해 서브 클래스 할 수있는 기본 클래스를 제공합니다. 이러한 클래스는 전송과 함께 사용됩니다. 프로토콜은 들어오는 데이터를 구문 분석하고 나가는 데이터의 쓰기를 요청하는 반면 전송은 실제 I / O 및 버퍼링을 담당합니다. 다음은 프로토콜의 세 가지 클래스입니다-
Protocol − 이것은 TCP 및 SSL 전송에 사용할 스트리밍 프로토콜을 구현하기위한 기본 클래스입니다.
DatagramProtocol − UDP 전송에 사용할 데이터 그램 프로토콜을 구현하기위한 기본 클래스입니다.
SubprocessProtocol − 이것은 일련의 단방향 파이프를 통해 자식 프로세스와 통신하는 프로토콜을 구현하기위한 기본 클래스입니다.
반응 형 프로그래밍은 데이터 흐름과 변화의 전파를 다루는 프로그래밍 패러다임입니다. 즉, 한 구성 요소에서 데이터 흐름을 내 보내면 변경 사항이 반응 프로그래밍 라이브러리에 의해 다른 구성 요소로 전파됩니다. 변경 전파는 최종 수신자에 도달 할 때까지 계속됩니다. 이벤트 기반 프로그래밍과 반응 형 프로그래밍의 차이점은 이벤트 기반 프로그래밍은 이벤트를 중심으로 진행되고 반응 형 프로그래밍은 데이터를 중심으로 진행된다는 것입니다.
반응 형 프로그래밍을위한 ReactiveX 또는 RX
ReactiveX 또는 Raective Extension은 반응 형 프로그래밍의 가장 유명한 구현입니다. ReactiveX의 작동은 다음 두 클래스에 따라 다릅니다.
관찰 가능한 클래스
이 클래스는 데이터 스트림 또는 이벤트의 소스이며 데이터가 한 스레드에서 다른 스레드로 전달 될 수 있도록 들어오는 데이터를 압축합니다. 일부 관찰자가 구독 할 때까지 데이터를 제공하지 않습니다.
관찰자 클래스
이 클래스는 observable. Observable이있는 여러 관찰자가있을 수 있으며 각 관찰자는 방출되는 각 데이터 항목을 수신합니다. 옵저버는 observable에 가입하여 세 가지 유형의 이벤트를받을 수 있습니다.
on_next() event − 데이터 스트림에 요소가 있음을 의미합니다.
on_completed() event − 배출 종료를 의미하며 더 이상 항목이 없습니다.
on_error() event − 또한 방출의 끝을 의미하지만 오류가 발생하는 경우 observable.
RxPY – 반응 형 프로그래밍을위한 Python 모듈
RxPY는 반응 형 프로그래밍에 사용할 수있는 Python 모듈입니다. 모듈이 설치되었는지 확인해야합니다. 다음 명령을 사용하여 RxPY 모듈을 설치할 수 있습니다.
pip install RxPY
예
다음은 사용하는 Python 스크립트입니다. RxPY 모듈 및 클래스 Observable 과 Observe for반응 형 프로그래밍. 기본적으로 두 가지 클래스가 있습니다-
get_strings() − 관찰자로부터 문자열을 얻기 위해.
PrintObserver()− 관찰자로부터 문자열을 인쇄합니다. 관찰자 클래스의 세 가지 이벤트를 모두 사용합니다. 또한 subscribe () 클래스를 사용합니다.
from rx import Observable, Observer
def get_strings(observer):
observer.on_next("Ram")
observer.on_next("Mohan")
observer.on_next("Shyam")
observer.on_completed()
class PrintObserver(Observer):
def on_next(self, value):
print("Received {0}".format(value))
def on_completed(self):
print("Finished")
def on_error(self, error):
print("Error: {0}".format(error))
source = Observable.create(get_strings)
source.subscribe(PrintObserver())
산출
Received Ram
Received Mohan
Received Shyam
Finished
반응 형 프로그래밍을위한 PyFunctional 라이브러리
PyFunctional반응 형 프로그래밍에 사용할 수있는 또 다른 Python 라이브러리입니다. 이를 통해 Python 프로그래밍 언어를 사용하여 기능적 프로그램을 만들 수 있습니다. 연결된 함수 연산자를 사용하여 데이터 파이프 라인을 만들 수 있기 때문에 유용합니다.
RxPY와 PyFunctional의 차이점
두 라이브러리 모두 반응 형 프로그래밍에 사용되며 유사한 방식으로 스트림을 처리하지만 두 라이브러리의 주요 차이점은 데이터 처리에 따라 다릅니다. RxPY 시스템의 데이터와 이벤트를 처리하는 동안 PyFunctional 함수형 프로그래밍 패러다임을 사용하는 데이터 변환에 중점을 둡니다.
PyFunctional 모듈 설치
이 모듈을 사용하기 전에 설치해야합니다. 다음과 같이 pip 명령을 사용하여 설치할 수 있습니다.
pip install pyfunctional
예
다음 예제 사용 the PyFunctional 모듈과 그 seq반복 및 조작 할 수있는 스트림 객체 역할을하는 클래스. 이 프로그램에서는 모든 값을 두 배로 늘리는 람다 함수를 사용하여 시퀀스를 매핑 한 다음 x가 4보다 큰 값을 필터링하고 마지막으로 시퀀스를 나머지 모든 값의 합계로 줄입니다.
from functional import seq
result = seq(1,2,3).map(lambda x: x*2).filter(lambda x: x > 4).reduce(lambda x, y: x + y)
print ("Result: {}".format(result))
산출
Result: 6