Python Language
파이썬 동시성
수색…
비고
파이썬 개발자는 threading
과 multiprocessing
간의 API가 유사하여 프로그래머가 두 변형을 쉽게 전환 할 수 있도록했습니다.
스레딩 모듈
from __future__ import print_function
import threading
def counter(count):
while count > 0:
print("Count value", count)
count -= 1
return
t1 = threading.Thread(target=countdown,args=(10,))
t1.start()
t2 = threading.Thread(target=countdown,args=(20,))
t2.start()
같은 CPython과 같은 파이썬의 특정 구현에서, 진정한 병렬 때문에 GIL로 알려져있다, 또는 G 내가 L의 옥토퍼스를 nterpreter lobal 무엇을 사용하는 스레드를 사용하여 달성되지 않는다.
다음은 Python 동시성에 대한 훌륭한 개요입니다.
David Beazley (YouTube)의 파이썬 동시성
다중 처리 모듈
from __future__ import print_function
import multiprocessing
def countdown(count):
while count > 0:
print("Count value", count)
count -= 1
return
if __name__ == "__main__":
p1 = multiprocessing.Process(target=countdown, args=(10,))
p1.start()
p2 = multiprocessing.Process(target=countdown, args=(20,))
p2.start()
p1.join()
p2.join()
여기서 각 기능은 새 프로세스에서 실행됩니다. 파이썬 VM의 새로운 인스턴스가 코드를 실행하기 때문에 GIL
이없고 다중 코어에서 병렬 처리가 실행된다.
Process.start
메소드는이 새 프로세스를 시작하고 인수 args
target
인수에 전달 된 함수를 실행합니다. Process.join
메서드는 프로세스 p1
과 p2
의 실행이 끝날 때까지 대기합니다.
새로운 프로세스는 python 버전과 코드가 실행되는 plateform에 따라 다르게 실행됩니다. 예 :
- Windows는
spawn
을 사용하여 새 프로세스를 만듭니다. - 유닉스 시스템과 버전 3.3 이전의 프로세스는
fork
사용하여 생성됩니다.
이 방법은 POSIX에서 fork를 사용하지 않으므로 다른 멀티 프로세싱 라이브러리와 상호 작용할 때 예기치 않은 동작을 유발합니다. - Unix 시스템 및 버전
forkserver
이상에서는 프로그램 시작시multiprocessing.set_start_method
를 사용하여fork
,forkserver
또는spawn
새 프로세스를 시작할 수 있습니다.forkserver
및spawn
메서드는 분기하는 것보다 느리지 만 예상치 못한 동작을 방지합니다.
POSIX 포크 사용법 :
다중 쓰레드 프로그램에서 포크 후, 자식은 execve를 호출 할 때까지 async-signal-safe 함수 만 안전하게 호출 할 수 있습니다.
( 참조 )
포크를 사용하면 모든 현재 뮤텍스에 대해 똑같은 상태로 새로운 프로세스가 시작되지만 MainThread
만 시작됩니다. 이 경쟁 조건 등으로 이어질 수 이것은 안전하지 않은 것입니다 :
-
MainThread
에서Lock
을 사용하고 그것을 어느 시점에서 잠글 것으로 가정되는 다른 스레드로 전달하는 경우.fork
동시에 발생하면 새 프로세스는 두 번째 스레드가이 새 프로세스에 존재하지 않으므로 잠금 해제되지 않습니다.
사실, 순수한 파이썬에서는 multiprocessing
이 제대로 처리하기 때문에 이러한 종류의 동작이 발생하지 않아야하지만, 다른 라이브러리와 상호 작용하는 경우 이러한 종류의 동작이 발생할 수 있습니다 (예 : numOS / accelerated on macOS).
다중 처리 프로세스간에 데이터 전달
두 스레드간에 처리 할 때 데이터가 민감하므로 (동시 읽기와 동시 쓰기가 서로 충돌하여 경쟁 조건이 발생할 수 있다고 생각하기 때문에) 스레드간에 데이터를 쉽게주고받을 수 있도록 고유 한 개체 집합이 만들어졌습니다. 모든 실제 작업은 스레드간에 사용할 수 있지만 항상 Queue를 사용하는 것이 안전합니다.
import multiprocessing
import queue
my_Queue=multiprocessing.Queue()
#Creates a queue with an undefined maximum size
#this can be dangerous as the queue becomes increasingly large
#it will take a long time to copy data to/from each read/write thread
대부분의 사람들은 대기열을 사용할 때 항상 빈을 사용하는 대신 try : except : 블록에 대기열 데이터를 배치하도록 제안합니다. 그러나 스캔주기를 건너 뛰어도 상관하지 않는 응용 프로그램의 경우 ( queue.Empty==True
queue.Empty==False
) 상태를 전환하는 동안 데이터를 대기열에 배치 할 수 있습니다. 일반적으로 읽기를 배치하는 것이 좋습니다 'if'문은 예외를 잡는 것보다 기술적으로 더 효과적이기 때문에 Iftry 블록이라고 부르는 것에 쓰기 액세스를 허용해야합니다.
import multiprocessing
import queue
'''Import necessary Python standard libraries, multiprocessing for classes and queue for the queue exceptions it provides'''
def Queue_Iftry_Get(get_queue, default=None, use_default=False, func=None, use_func=False):
'''This global method for the Iftry block is provided for it's reuse and
standard functionality, the if also saves on performance as opposed to catching
the exception, which is expencive.
It also allows the user to specify a function for the outgoing data to use,
and a default value to return if the function cannot return the value from the queue'''
if get_queue.empty():
if use_default:
return default
else:
try:
value = get_queue.get_nowait()
except queue.Empty:
if use_default:
return default
else:
if use_func:
return func(value)
else:
return value
def Queue_Iftry_Put(put_queue, value):
'''This global method for the Iftry block is provided because of its reuse
and
standard functionality, the If also saves on performance as opposed to catching
the exception, which is expensive.
Return True if placing value in the queue was successful. Otherwise, false'''
if put_queue.full():
return False
else:
try:
put_queue.put_nowait(value)
except queue.Full:
return False
else:
return True