Day11 프로세스 및 스레드
다중 프로세스
multiprocessing
multiprocessing
모듈은 하나의 프로세스 대상을 대표하는 Process
클래스를 제공합니다. 다음 예는 하위 프로세스를 시작하고 끝날 때까지 기다리는 것을 보여 줍니다.from multiprocessing import Process
import os
#
def run_proc(name):
print('Run child process %s (%s)...' % (name, os.getpid()))
# getpid 。 。
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Process(target=run_proc, args=('test',))
print('Child process will start.')
p.start()
p.join()
print('Child process end.')
Parent process 13932.
Child process will start.
Run child process test (8892)...
Child process end.
# p.join() :
Parent process 12172.
Child process will start.
Child process end.
Run child process test (15952)...
하위 프로세스를 만들 때 실행 함수와 함수의 매개 변수를 입력하고
Process
실례를 만들어서 start()
방법으로 시작합니다.join()
방법은 하위 프로세스가 끝난 후에 계속 아래로 운행할 수 있으며 보통 프로세스 간의 동기화에 사용된다.Pool
많은 수의 하위 프로세스를 시작하는 경우 프로세스 풀을 사용하여 하위 프로세스를 대량 생성할 수 있습니다.
from multiprocessing import Pool
import os, time, random
def long_time_task(name):
print('Run task %s (%s)...' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print('Task %s runs %0.2f seconds.' % (name, (end - start)))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Pool(4)
for i in range(5):
p.apply_async(long_time_task, args=(i,))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')
Parent process 12232.
Waiting for all subprocesses done...
Run task 0 (17872)...
Run task 1 (320)...
Run task 2 (7952)...
Run task 3 (18048)...
Task 1 runs 0.09 seconds.
Run task 4 (320)...
Task 3 runs 1.26 seconds.
Task 4 runs 1.47 seconds.
Task 0 runs 2.01 seconds.
Task 2 runs 2.91 seconds.
All subprocesses done.
Parent process 17652.
Waiting for all subprocesses done...
Run task 0 (7632)...
Run task 1 (14548)...
Run task 2 (1300)...
Run task 3 (1104)...
Task 0 runs 0.33 seconds.
Run task 4 (7632)...
Task 3 runs 0.58 seconds.
Task 2 runs 2.52 seconds.
Task 1 runs 2.91 seconds.
Task 4 runs 2.71 seconds.
All subprocesses done.
코드 판독:
Pool
대상에 대한 호출join()
방법은 모든 하위 프로세스가 실행되기를 기다릴 것입니다. 호출join()
전에 반드시 호출close()
을 해야 합니다. 호출close()
후에는 새로운 Process
을 추가할 수 없습니다.출력의 결과는task0
,1
,2
,3
는 즉각 실행되고,task4
는 앞의 어떤task가 완성된 후에야 실행됩니다. 이것은 Pool
의 기본 크기가 내 컴퓨터에서 4이기 때문에 최대 4(CPU의 핵 수)의 프로세스를 동시에 실행해야 합니다.프로세스 간 통신
Process
간에 통신이 필요한 것이 틀림없고 운영체제는 프로세스 간의 통신을 실현하기 위해 많은 메커니즘을 제공했다.파이톤multiprocessing
모듈은 밑바닥의 메커니즘을 포장하고 Queue
,Pipes
등 다양한 방식으로 데이터를 교환했다.우리는 Queue
를 예로 들면 부모 프로세스에서 두 개의 하위 프로세스를 만들고 하나는 Queue
에 데이터를 쓰고 하나는 Queue
에서 데이터를 읽는다.from multiprocessing import Process, Queue
import os, time, random
#
def write(q):
print('Process to write:%s'%os.getpid())
for value in ['A','B','C']:
print('Put %s to queue...'%value)
q.put(value)
time.sleep(random.random())
#
def read(q):
print('Process to read %s'%os.getpid())
while True:
value = q.get(True)
print('Get %s from queue.'% value)
if __name__ == '__main__':
# Queue, :
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
# pw, :
pw.start()
# pr, :
pr.start()
# pw :
pw.join()
# pr , , :
pr.terminate()
C:\Python36x32bit>python practice.py
Process to write:11828
Put A to queue...
Process to read 4232
Get A from queue.
Put B to queue...
Get B from queue.
Put C to queue...
Get C from queue.
# pw.join() :
Process to write:12076
Put A to queue...
Put B to queue...
Put C to queue...
다중 스레드
다중 임무는 다중 프로세스로 완성할 수도 있고, 한 프로세스 내의 다중 프로세스로 완성할 수도 있다.앞에서 언급한 바와 같이 프로세스는 몇 개의 라인으로 구성되어 있으며, 한 프로세스는 적어도 하나의 라인이 있다.루틴은 운영체제가 직접 지원하는 실행 단원이기 때문에 고급 언어는 보통 다중 루틴 지원을 내장하고 Python도 예외가 아니다. 또한 Python의 루틴은 아날로그된 루틴이 아니라 진정한 Posix Thread이다.파이톤의 표준 라이브러리는 두 개의 모듈을 제공했다.
_thread
와 threading
, _thread
는 저급 모듈, threading
는 고급 모듈로 _thread
를 봉했다.절대 다수의 상황에서 우리는 threading
라는 고급 모듈만 사용할 수 있다.하나의 라인을 시작하면 함수를 불러와서 Thread
실례를 만들고 start()
호출해서 실행합니다.import time,threading
# :
def loop():
print('thread %s is running...' % threading.current_thread().name)
n = 0
while n < 5:
n += 1
print('thread %s >>> %s'% (threading.current_thread().name,n))
time.sleep(1)
print('thread %s ended.' % threading.current_thread().name)
print('thread %s is running...'% threading.current_thread().name)
t = threading.Thread(target=loop,name='LoopThread')
t.start()
t.join()
print('thread %s ended.'% threading.current_thread().name)
>>>
=================== RESTART: C:\Python36x32bit\practice.py ===================
thread MainThread is running...
thread LoopThread is running...
thread LoopThread >>> 1
thread LoopThread >>> 2
thread LoopThread >>> 3
thread LoopThread >>> 4
thread LoopThread >>> 5
thread LoopThread ended.
thread MainThread ended.
모든 프로세스가 기본적으로 하나의 라인을 시작하기 때문에, 우리는 이 라인을 주 라인이라고 부른다. 주 라인은 새로운 라인을 시작할 수 있다. 파이톤의
threading
모듈에는 current_thread()
함수가 있는데, 이것은 현재 라인의 실례를 영원히 되돌려준다.주 루틴의 실례적인 이름은 MainThread
이고, 하위 루틴의 이름은 만들 때 지정되며, 우리는 LoopThread
로 하위 루틴을 명명합니다.이름은 인쇄할 때만 표시됩니다. 다른 의미는 없습니다. 이름이 없으면 Thread-1
, Thread-2
...다중 프로세스와 다중 프로세스의 가장 큰 차이점은 다중 프로세스에서 같은 변수는 각각 하나의 복사본이 모든 프로세스에 존재하고 서로 영향을 주지 않는다는 것이다. 그러나 다중 프로세스에서 모든 변수는 모든 프로세스가 공유하기 때문에 모든 변수는 하나의 프로세스에 의해 수정될 수 있다. 따라서 프로세스 간에 데이터를 공유하는 가장 큰 위험은 여러 프로세스가 동시에 변수를 바꾸어 내용을 어지럽히는 데 있다.여러 개의 라인이 하나의 변수를 동시에 조작하는데 어떻게 내용을 어지럽혔는지 살펴보자.
import time, threading
# :
balance = 0
def change_it(n):
# , 0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(100000):
change_it(n)
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
우리는 공유 변수
balance
를 정의했는데 초기값은0
이고 두 개의 라인을 시작하여 먼저 저장하고 나중에 취한다. 이론적인 결과는 0
이어야 한다. 그러나 라인의 스케줄링은 운영체제에 의해 결정되기 때문에 t1, t2가 교체되어 집행될 때 순환 횟수가 충분하기만 하면 balance
의 결과는 반드시 0
가 아니다.왜냐하면 고급 언어의 한 문장이 CPU에서 실행될 때 몇 개의 문장이기 때문이다. 간단한 계산이라도 balance = balance + n
은 두 단계로 나뉜다. 계산balance + n
은 임시 변수에 저장한다.임시 변수의 값을 balance
부여합니다.즉,x = balance + n
balance = x
x는 부분적인 변수이기 때문에 두 스레드는 각각 자신의 x를 가지고 코드가 정상적으로 실행될 때:
balance = 0
t1: x1 = balance + 5 # x1 = 0 + 5 = 5
t1: balance = x1 # balance = 5
t1: x1 = balance - 5 # x1 = 5 - 5 = 0
t1: balance = x1 # balance = 0
t2: x2 = balance + 8 # x2 = 0 + 8 = 8
t2: balance = x2 # balance = 8
t2: x2 = balance - 8 # x2 = 8 - 8 = 0
t2: balance = x2 # balance = 0
balance = 0
그러나 t1과 t2는 번갈아 운행한다. 만약 운영체제가 아래의 순서로 t1, t2를 실행한다면
balance = 0
t1: x1 = balance + 5 # x1 = 0 + 5 = 5
t2: x2 = balance + 8 # x2 = 0 + 8 = 8
t2: balance = x2 # balance = 8
t1: balance = x1 # balance = 5
t1: x1 = balance - 5 # x1 = 5 - 5 = 0
t1: balance = x1 # balance = 0
t2: x2 = balance - 8 # x2 = 0 - 8 = -8
t2: balance = x2 # balance = -8
balance = -8
그 이유는 수정
balance
이 여러 개의 문장을 필요로 하기 때문에 이 몇 개의 문장을 집행할 때 노선이 중단될 수 있기 때문에 여러 개의 노선이 같은 대상의 내용을 어지럽혔기 때문이다.만약 우리가 balance
의 계산이 정확하다는 것을 확보하려면 change_it()
에 자물쇠를 주어야 한다. 어떤 라인이 실행되기 시작할 때change_it()
우리는 이 라인이 자물쇠를 얻었기 때문에 다른 라인은 동시에 실행할 수 없다change_it()
. 자물쇠가 풀린 후에야 이 자물쇠를 얻은 후에야 바꿀 수 있다고 말한다.자물쇠가 하나밖에 없기 때문에 아무리 라인이 많더라도 같은 시간에 최대 한 라인만 이 자물쇠를 가지고 있기 때문에 수정된 충돌을 일으키지 않습니다.자물쇠를 만드는 것은 threading.Lock()
를 통해 이루어진다.import time,threading
# :
balance = 0
lock = threading.Lock()
def change_it(n):
# , 0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(100000):
# :
lock.acquire()
try:
# :
change_it(n)
finally:
#
lock.release()
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
여러 개의 루틴이 동시에 실행될 때
lock.acquire()
, 단 한 개의 루틴만이 자물쇠를 성공적으로 얻은 후에 코드를 계속 실행하고, 다른 루틴은 자물쇠를 얻을 때까지 계속 기다린다.자물쇠를 얻은 라인을 다 사용한 후에 반드시 자물쇠를 풀어야 한다. 그렇지 않으면 자물쇠를 기다리는 라인은 영원히 기다리며 죽은 라인이 될 것이다.그래서 우리는 try...finally
로 자물쇠가 반드시 풀릴 것을 확보한다.자물쇠의 장점은 특정한 관건적인 코드가 한 라인에서만 처음부터 끝까지 완전하게 집행될 수 있다는 것을 확보하는 것이다. 나쁜 점도 당연히 많다. 우선 여러 라인이 동시에 집행되는 것을 막는다. 자물쇠를 포함하는 특정한 코드는 사실상 단일 라인 모델로만 집행할 수 있고 효율이 크게 떨어진다.그 다음으로 여러 개의 자물쇠가 존재할 수 있기 때문에 서로 다른 라인이 서로 다른 자물쇠를 가지고 상대방이 가지고 있는 자물쇠를 얻으려고 할 때 사라진 자물쇠를 초래하여 여러 라인이 모두 걸려 있고 실행할 수도 없고 끝낼 수도 없으며 운영체제에 의해 강제로 종료될 수 있다.ThreadLocal
: import threading
# ThreadLocal :
local_school = threading.local()
def process_student():
# student:
std = local_school.student
print('Hello, %s (in %s)' % (std, threading.current_thread().name))
def process_thread(name):
# ThreadLocal student:
local_school.student = name
process_student()
t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
Hello, Alice (in Thread-A)
Hello, Bob (in Thread-B)
전역 변수
local_school
는 하나의 대상ThreadLocal
으로 각Thread
는 그것에 대해 읽기와 쓰기student
속성을 가질 수 있지만 서로 영향을 주지 않는다.너는 local_school
를 전역 변수로 볼 수 있지만 모든 속성은 local_school.student
라인의 국부 변수로 임의로 읽고 쓸 수 있으며 서로 간섭하지 않으며 자물쇠를 관리하지 않아도 된다ThreadLocal
내부에서 처리할 것이다.전역 변수local_school
는 하나dict
로 이해할 수 있으며 local_school.student
뿐만 아니라 다른 변수도 귀속할 수 있다. 예를 들어 local_school.teacher
등이다.ThreadLocal
가장 자주 사용하는 곳은 모든 라인에 데이터베이스 연결, HTTP 요청, 사용자 신분 정보 등이다. 이런 라인의 모든 호출된 처리 함수는 이 자원에 쉽게 접근할 수 있다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.