English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
최근 Python 병행을 공부하면서 다중 프로세스, 다중 스레드, 비동기 및 고리를 요약했습니다.
1. 다중 스레드
다중 스레드는 하나의 프로세스 내에 여러 컨트롤러가 존재할 수 있게 하여 여러 함수가 동시에 활성화되도록 하여 여러 함수의 작업이 동시에 실행될 수 있습니다. 심지어 단일 CPU의 컴퓨터도 다른 스레드의 명령어 간에서 끊임없이 전환하여 다중 스레드가 동시에 실행되는 효과를 만들 수 있습니다.
다중 스레드는 병행 시스템과 같습니다. 병행 시스템은 일반적으로 여러 작업을 동시에 실행합니다. 여러 작업이 자원을 공유할 수 있을 때, 특히 특정 변수에 동시에 쓰일 때, 동기화 문제를 해결해야 합니다. 예를 들어, 다중 스레드 철도 티켓 판매 시스템에서는 하나의 명령어가 티켓이 판매되었는지 확인하고, 다른 하나의 명령어가 여러 창구가 동시에 티켓을 판매하면 존재하지 않는 티켓을 판매할 수 있습니다.
동기화 상황에서, 명령어 실행의 순서는 커널이 결정합니다. 동일한 스레드 내에서 명령어는 순서대로 실행되지만, 다른 스레드 간의 명령어 실행 순서는 명확하지 않습니다. 따라서 다중 스레드 동기화 문제를 고려해야 합니다. 동기화(synchronization)는 특정 시간 동안 특정 자원에 대한 접근을 허용하는 것입니다.
1thread 모듈
2threading 모듈
threading.Thread을 통해 스레드를 생성합니다.
여분의 티켓이 있는지 판단하고 티켓을 판매하는 것에 대해 mutual exclusion lock을 추가하여, 한 스레드가 여분의 티켓이 없다고 판단한 후 다른 스레드가 티켓 판매 작업을 수행하는 일이 발생하지 않도록 합니다.
#! /usr/bin/python #-* coding: utf-8 -* # __author__ ="tyomcat" import threading import time import os def booth(tid): global i global lock while True: lock.acquire() if i!=0: i=i-1 print "Window:", tid, ", Remaining tickets:", i time.sleep(1) else: print "Thread_id", tid, "No more tickets" os._exit(0) lock.release() time.sleep(1) i = 100 lock=threading.Lock() for k in range(10): new_thread = threading.Thread(target=booth, args=(k,)) new_thread.start()
2. 코루틴(또는 마이크로 스레드, 파이프라인)
코루틴은 스레드의 강제적 스케줄링과 달리 협력적 스케줄링입니다. 코루틴은 단일 스레드지만, 원래 비동기적으로 사용할 필요가 있었던 것을 사용할 수 있습니다.+콜백 방식으로 작성된 비인간적인 코드는 보여지는 것처럼 동기적으로 작성할 수 있습니다.
1코루틴은 python에서 제너레이터(제너레이터)로 구현될 수 있습니다.
먼저 제너레이터와 yield에 대한 깊은 이해가 필요합니다.
일반적인 python 함수를 호출할 때, 일반적으로 함수의 첫 번째 코드 행에서 시작하여 return 문, 예외 또는 함수 실행(또는 암시적으로 None를 반환)에 도달할 때까지 실행됩니다.
함수가 제어권을 호출자에게 반환하면 모든 것이 끝납니다. 하지만有时는 시퀀스를 생성할 수 있는 함수를 만들어 "자신의 작업을 저장"할 수 있습니다. 이는 제너레이터(함수에 yield 키워드를 사용한 것)입니다.
함수가 일반적인 의미와는 달리 반환하지 않기 때문에 "시퀀스를 생성할 수 있다"는 것은 가능합니다. return의 잠재적인 의미는 함수가 실행 코드의 제어권을 호출된 함수의 위치로 반환하는 것입니다. 반면에 "yield"의 잠재적인 의미는 제어권의 이전은 일시적이고 자발적이며, 함수는 앞으로 제어권을 되찾을 것입니다.
看一下生产者/消费者的例子:
#! /usr/bin/python #-* coding: utf-8 -* # __author__ ="tyomcat" import time import sys # 生产者 def produce(l): i=0 while 1: if i < 10: l.append(i) yield i i=i+1 time.sleep(1) else: return # 消费者 def consume(l): p = produce(l) while 1: try: p.next() while len(l) > 0: print l.pop() except StopIteration: sys.exit(0) if __name__ == "__main__": l = [] consume(l)
当程序执行到produce的yield i时,返回了一个generator并暂停执行,当我们在custom中调用p.next(),程序又返回到produce的yield i 继续执行,这样 l 中又append了元素,然后我们print l.pop(),直到p.next()引发了StopIteration异常。
2、Stackless Python
3、greenlet模块
基于greenlet的实现则性能仅次于Stackless Python,大致比Stackless Python慢一倍,比其他方案快接近一个数量级。其实greenlet不是一种真正的并发机制,而是在同一线程内,在不同函数的执行代码块之间切换,实施“你运行一会、我运行一会”,并且在进行切换时必须指定何时切换以及切换到哪。
4、eventlet模块
三、多进程
1子进程(subprocess包)
python에서 subprocess 패키지를 통해 child 프로세스를 for크하고 외부 프로그램을 실행합니다.
시스템 명령어를 호출할 때, 가장 먼저 고려하는 것은 os 모듈입니다. os.system()와 os.popen()를 통해 작업을 수행합니다. 하지만 이 두 명령어는 매우 간단하여 복잡한 작업을 완료할 수 없습니다. 예를 들어, 실행 중인 명령어에 입력을 제공하거나 명령어의 출력을 읽거나, 명령어의 실행 상태를 판단하거나, 여러 명령어의 병행을 관리하는 것 등입니다. 이 때 subprocess 모듈의 Popen 명령어는 우리가 필요한 작업을 효과적으로 완료할 수 있습니다.
>>> import subprocess >>> command_line = raw_input() ping -c 10 www.baidu.com >>> args = shlex.split(command_line) >>> p = subprocess.Popen(args)
subprocess.PIPE를 사용하여 여러个子 프로세스의 입력과 출력을 연결하여 파이프(pipe)를 구성합니다:
import subprocess child1 child = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE) child2 child = subprocess.Popen(["wc"], stdin=child1.stdout, stdout=subprocess.PIPE) out = child2.communicate() print(out)
communicate() 메서드는 stdout과 stderr에서 데이터를 읽어 stdin에 입력합니다.
2)、다중 프로세스 (multiprocessing 패키지)
(1)、multiprocessing 패키지는 Python의 다중 프로세스 관리 패키지입니다. threading.Thread과 유사하게, multiprocessing.Process 객체를 사용하여 프로세스를 생성할 수 있습니다.
프로세스 풀 (Process Pool)은 여러 개의 프로세스를 생성할 수 있습니다.
apply_async(func,args) 프로세스 풀에서 프로세스를 꺼내 func을 실행합니다. args는 func의 매개변수입니다. 그러면 AsyncResult 객체를 반환하며, 이 객체에 get() 메서드를 호출하여 결과를 얻을 수 있습니다.
close() 프로세스 풀은 새로운 프로세스를 더 이상 생성하지 않습니다.
join() 프로세스 풀의 모든 프로세스를 기다립니다. join()를 호출하기 전에 Pool에 close() 메서드를 호출해야 합니다.
#! /usr/bin/env python # -*- coding:utf-8 -*- # __author__ == "tyomcat" # "저의 컴퓨터는4개 cpu" from multiprocessing import Pool import os, time def long_time_task(name): print 'Run task %s (%s)...' % (name, os.getpid()) start = time.time() time.sleep(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() for i in range(4): p.apply_async(long_time_task, args=(i,)) print 'Waiting for all subprocesses done...' p.close() p.join() print 'All subprocesses done.'
(2)、多进程共享资源
shared memory와 Manager 객체를 통해: 서버로서의 프로세스를 사용하여 Manager를 통해 실제 자원을 저장합니다.
기타 프로세스는 매개변수를 통해 또는 주소에 따라 Manager에 접근할 수 있으며, 연결을 통해 서버 상의 자원을操作할 수 있습니다.
#! /usr/bin/env python # -*- coding:utf-8 -*- # __author__ == "tyomcat" from multiprocessing import Queue,Pool import multiprocessing,time,random def write(q): for value in ['A','B','C','D']: print "Put %s to Queue!" % value q.put(value) time.sleep(random.random()) def read(q,lock): while True: lock.acquire() if not q.empty(): value=q.get(True) print "Get %s from Queue" % value time.sleep(random.random()) else: break lock.release() if __name__ == "__main__": manager=multiprocessing.Manager() q=manager.Queue() p=Pool() lock=manager.Lock() pw=p.apply_async(write,args=(q,)) pr=p.apply_async(read,args=(q,lock)) p.close() p.join() 출력 모든 데이터가 입력되고 읽혀졌음을 출력합니다
4. 비동기
스레드나 프로세스가 사용하는 것은 동기 숫자이며, 블로킹이 발생하면 성능이 대폭 감소하고, CPU의 잠재력을 최대한 활용할 수 없으며, 하드웨어 투자를 낭비하고, 더 중요한 것은 소프트웨어 모듈의 철박화와 밀착화를 초래하여, 이후 확장과 변화에 불리합니다.
프로세스나 스레드가 막히거나 전환될 때마다 시스템 호출(system call)에 빠지며, 먼저 CPU가 운영 체제의 스케줄러를 실행하고, 그 다음 스케줄러가 실행할 프로세스(스레드)를 결정합니다. 여러 스레드 간에 일부 액세스 mutual 코드를 처리할 때에는 락을 추가해야 합니다.
현재 유행하는 비동기 서버는 대부분 이벤트 드라이브 기반입니다(예: nginx).
비동기 이벤트 드라이브 모델에서는, 블로킹 작업을 비동기 작업으로 변환합니다. 메인 스레드는 이 비동기 작업을 발동하고, 이 비동기 작업의 결과를 처리합니다. 모든 블로킹 작업이 비동기 작업으로 변환되기 때문에, 이 모델의 주 스레드는 대부분 실제 계산 작업을 처리하며, 다중 스레드의 조정 시간이 줄어들어, 이 모델의 성능이 일반적으로 좋습니다.
이것이 본문의 모든 내용입니다. 많은 도움이 되었기를 바랍니다. 또한, 나리아 강의에 많은 지지를 부탁드립니다.
언급: 본문은 인터넷에서 가져왔으며, 저작권자는 모두 소유합니다. 내용은 인터넷 사용자가 자발적으로 기여하고 업로드한 것이며, 사이트는 소유권을 가지지 않으며, 인공 편집 처리를하지 않으며, 관련 법적 책임도 부담하지 않습니다. 저작권 침해 내용이 있음을 발견하면 notice#w로 이메일을 보내 주세요.3codebox.com에 대한 신고를 보내면 (#을 @으로 변경하세요) 관련 증거를 제공하면, 사이트는 즉시 저작권 침해 내용을 제거합니다.