進程同步( 使用Queue&JoinableQueue)
#coding=utf-8
import multiprocessing
import time
class Consumer(multiprocessing.Process):
def __init__(self,task_queue,result_queue):
multiprocessing.Process.__init__(self)
self.task_queue = task_queue
self.result_queue = result_queue
def run(self):
proc_name = self.name
while True:
next_task =self.task_queue.get()
if next_task is None:
print("%s Exiting" %proc_name)
self.task_queue.task_done()
break
print("%s:%s" %(proc_name,next_task))
answer = next_task()
self.task_queue.task_done()
self.result_queue.put(answer)
return
class Task(object):
def __init__(self,a,b):
self.a = a
self.b = b
def __call__(self):
time.sleep(0.1)
return "%s * %s = %s" %(self.a,self.b,self.a * self.b)
def __str__(self):
return "%s * %s " %(self.a,self.b)
if __name__ == "__main__":
tasks = multiprocessing.JoinableQueue()
results = multiprocessing.Queue()
num_consumers = multiprocessing.cpu_count()
print("Creating %d consumer" %num_consumers)
consumers = [Consumer(tasks,results) for i in range(num_consumers)]
for w in consumers:
w.start()
num_jobs = 10
for i in range(num_jobs):
tasks.put(Task(i,i))
for i in range(num_consumers):
tasks.put(None)
tasks.join()
while num_jobs:
result = results.get()
print("Result: %s" %result)
num_jobs -= 1
進程同步(加鎖-Lock)
不加鎖
加鎖
鎖是爲了確保數據一致性,比如讀寫鎖,每個進程給一個變量增加 1 ,但是如果在一個進程讀取但還沒有寫入的時候,另外的進程也同時讀取了,並寫入該值,則最後寫入的值是錯誤的,這時候就需要加鎖來保持數據一致性
進程同步(加鎖-Semaphore)
Semaphore用於控制對共享資源的訪問數量。Semaphore鎖和Lock稍有不同,Semaphore相當於N把鎖,獲取其中一把就可以執行。可用鎖的總數N在創建實例時傳入,比如s = Semaphore(n)。與Lock一樣,如果可用鎖爲0,進程將會阻塞,直到可用鎖大於0。
#coding=utf-8
import multiprocessing
import time
def worker(s,i):
s.acquire()
print(multiprocessing.current_process().name + "acquire")
time.sleep(i)
print(multiprocessing.current_process().name + "release")
s.release()
if __name__== "__main__":
s = multiprocessing.Semaphore(3)
for i in range(5):
p = multiprocessing.Process(target=worker,args=(s,i*2))
p.start()
進程同步(信號傳遞-Event)
Event提供一種簡單的方法,可以在進程間傳遞狀態信息,實現進程間同步通信。事件可以切換設置和未設置狀態。通過使用一個可選的超時值,時間對象的用戶可以等待其狀態從未設置變爲設置。
#coding=utf-8
import multiprocessing
import time
def wait_for_event(e):
print("wait_for_event: starting")
e.wait() #等待收到能執行信號,如果一直未收到將一直阻塞,死等
print("wait_for_event: e.is_set()->",e.is_set())
print("可以繼續做一些事情了。")
def wait_for_event_timeout(e,t):
print("wait_for_event_timeout: starting")
e.wait(t)#等待t秒超時,此時Event的狀態仍未未設置 print("wait_for_event_timeout: e.is_set()->",e.is_set())
e.set()#設置Event的狀態
print("信號狀態已被設置")
if __name__ == "__main__":
e = multiprocessing.Event()
print("begin,e.is_set()",e.is_set())
p1 = multiprocessing.Process(name = "block",target=wait_for_event,args=(e,))
p1.start()
p2 = multiprocessing.Process(name = "nonblock",target=wait_for_event_timeout,args=(e,2))
p2.start()
time.sleep(3)
print("main: event is set")
進程同步(使用管道-Pipe)
Pipe是兩個進程間通信的工具。Pipe可以是單向(half-duplex),也可以是雙向(duplex)。我們通過mutiprocessing.Pipe(duplex=False)創建單向管道 (默認爲雙向)。一個進程從PIPE一端輸入對象,然後被PIPE另一端的進程接收,單向管道只允許管道一端的進程輸入,而雙向管道則允許從兩端輸入。Pipe的每個端口同時最多一個進程讀寫,否則會出現各種問題,可能造成corruption異常。Pipe對象建立的時候,返回一個含有兩個元素的元組對象,每個元素代表Pipe的一端(Connection對象)。我們對Pipe的某一端調用send()方法來傳送對象,在另一端使用recv()來接收。
#coding=utf-8
import multiprocessing as mp
def proc_1(pipe):
pipe.send("hello")
print("proc_1 received: %s" %pipe.recv())
pipe.send("what is your name? ")
print("proc_1 received: %s " %pipe.recv())
def proc_2(pipe):
print("proc_2 received : %s" %pipe.recv())
pipe.send("hello ,too")
print("proc_2 received: %s" %pipe.recv())
pipe.send("I don't tell you!")
if __name__ == "__main__":
pipe = mp.Pipe()
print(len(pipe))
print(type(pipe))
p1 = mp.Process(target=proc_1,args=(pipe[0],))
p2 = mp.Process(target=proc_2,args=(pipe[1],))
p2.start()
p1.start()
p1.join()
p2.join()
#coding=utf-8
import multiprocessing as mp
from multiprocessing import Process,Lock
def write_file(content,lock,file_path="e:\\a.txt"):
lock.acquire() #保證同時只有一個進程在寫文件
with open(file_path,"a") as fp:
fp.write(content + "\n")
lock.release()
def proc_1(pipe,lock):
pipe.send("hello")
info = pipe.recv()
print("proc_1 recieved: %s" %info)
write_file(info,lock)
pipe.send("what is your name?")
info = pipe.recv()
print("proc_1 recieved: %s" %info)
write_file(info,lock)
def proc_2(pipe,lock):
info = pipe.recv()
print("proc_2 recieved: %s " %info)
write_file(info,lock)
pipe.send("hello too")
info = pipe.recv()
print("proc_2 recieved: %s" %info)
write_file(info,lock)
pipe.send("I don't tell you!")
if __name__ == "__main__":
pipe = mp.Pipe()#創建一個管道對象,爲一個元組,包含了pipe的兩端
lock = Lock()#創建一個鎖對象
print(len(pipe))
print(type(pipe))
#創建兩個子進程
p1 = mp.Process(target=proc_1,args=(pipe[0],lock))
p2 = mp.Process(target=proc_2,args=(pipe[1],lock))
p1.start()
p2.start()
p1.join()
p2.join()
#coding=utf-8
import multiprocessing as mp
from multiprocessing import Process,Lock
def write_file(content,lock,file_path="e:\\a.txt"):
lock.acquire()
with open(file_path,"a") as fp:
fp.write(content + "\n")
lock.release()
def proc_1(pipe,lock):
pipe.send("hello")
info = pipe.recv()
print("proc_1 recieved: %s" %info)
write_file(info,lock)
pipe.send("what is your name?")
info = pipe.recv()
print("proc_1 recieved: %s" %info)
write_file(info,lock)
def proc_2(pipe,lock):
info = pipe.recv()
print("proc_2 recieved: %s " %info)
write_file(info,lock)
pipe.send("hello too")
info = pipe.recv()
print("proc_2 recieved: %s" %info)
write_file(info,lock)
pipe.send("I don't tell you!")
if __name__ == "__main__":
p1_list = []
p2_list = []
pipe = mp.Pipe()
lock = Lock()
print(len(pipe))
print(type(pipe))
for i in range(5):
p1_list.append(mp.Process(target=proc_1,args=(pipe[0],lock)))
for i in range(5):
p2_list.append(mp.Process(target=proc_2,args=(pipe[1],lock)))
for i in range(5):
p1_list[i].start()
p2_list[i].start()
for i in range(5):
p1_list[i].join()
p2_list[i].join()
print("***********")
進程同步(使用Condition)
一個condition變量總是與某些類型的鎖相聯繫,當幾個condition變量必須共享同一個鎖的時候,是很有用的。鎖是conditon對象的一部分:沒有必要分別跟蹤。condition變量服從上下文管理協議:with語句塊封閉之前可以獲取與鎖的聯繫。 acquire() 和release() 會調用與鎖相關聯的相應方法。
wait()方法會釋放鎖,當另外一個進程使用notify() or notify_all()喚醒它之前會一直阻塞。一旦被喚醒,wait()會重新獲得鎖並返回,Condition類實現了一個conditon變量。這個condition變量允許一個或多個進程等待,直到他們被另一個進程通知。如果lock參數,被給定一個非空的值,那麼他必須是一個lock或者Rlock對象,它用來做底層鎖。否則,會創建一個新的Rlock對象,用來做底層鎖。
wait(timeout=None) :等待通知,或者等到設定的超時時間。當調用這wait()方法時,如果調用它的進程沒有得到鎖,那麼會拋出一個RuntimeError異常。 wait()釋放鎖以後,在被調用相同條件的另一個進程用notify() or notify_all() 叫醒之前會一直阻塞如果有等待的進程,notify()方法會喚醒一個在等待conditon變量的進程。
notify_all() 則會喚醒所有在等待conditon變量的進程。
注意:
notify()和notify_all()不會釋放鎖,也就是說,進程被喚醒後不會立刻返回他們的wait() 調用。除非進程調用notify()和notify_all()之後放棄了鎖的所有權。
在典型的設計風格里,利用condition變量加鎖去允許訪問一些共享狀態,進程在獲取到它想得到的狀態前,會反覆調用wait()。修改狀態的進程在他們狀態改變時
調用 notify() or notify_all(),用這種方式,進程會儘可能的獲取到想要的一個等待者狀態
#coding=utf-8
import multiprocessing as mp
import time
def consumer(cond):
with cond:#此處獲得與鎖的聯繫
print("consumer before wait")
cond.wait() #釋放鎖
#程序在此一直等待,等待notify()或notify_all()喚醒
print("consumer after wait")
def producer(cond):
with cond:
print("producer before notifyAll")
cond.notify_all()#喚醒所有等待鎖的進程,此處爲通知消費者可以消費了
print("producer after notifyAll")
if __name__ == "__main__":
condition = mp.Condition()
p1 = mp.Process(name = "p1",target=consumer,args=(condition,))
p2 = mp.Process(name = "p2",target=consumer,args=(condition,))
p3 = mp.Process(name = "p3",target = producer,args = (condition,))
p1.start()
time.sleep(2)
p2.start()
time.sleep(2)
p3.start()
#注意消費者進程的啓動需要放在生產者啓動之前,否則會出現生產者已經notify的情況下,消費者會無限等待的情況
注意消費者進程的啓動需要放在生產者啓動之前,否則會出現生產者已經notify的情況下,消費者會無限等待的情況
示例:
#coding=utf-8
import multiprocessing as mp
import time
def consumer(cond):
with cond:
print("consumer before wait")
cond.wait()
print("consumer after wait")
def producer(cond):
with cond:
print("producer before notifyAll")
cond.notify_all()
print("producer after notifyAll")
if __name__ == "__main__":
condition = mp.Condition()
p1 = mp.Process(name = "p1",target=consumer,args=(condition,))
p2 = mp.Process(name = "p2",target=consumer,args=(condition,))
p3 = mp.Process(name = "p3",target = producer,args = (condition,))
p1.start()
time.sleep(2)
p3.start()
#p3#此處生產者進程放在了消費者進程p2的前面,注意查看輸出
time.sleep(2)
p2.start()
多進程間共享數字變量
未使用共享變量
#coding=utf-8
from multiprocessing import Process
def f(n,a):
n = 3.1415
for i in range(len(a)):
a[i] = -a[i]
if __name__ == "__main__":
num = 0 #主進程的num
arr = list(range(10))#主進程的arr
p = Process(target = f,args=(num,arr))#修改了傳入f函數的值,但是數據屬於子進程,主進程不能訪問
p.start()
p.join()
print(num)
print(arr[:])
結果爲:主進程變量的值
注意:
此程序存在兩個進程 主進程和創建的子進程p,兩個進程的數據是相互獨立的,if __name__ == '__main__':模塊下的num,arr屬於主進程,這兩個參數被傳入子進程執行的函數後,存在於子進程的空間內,和主進程不影響;
使用共享變量
在上例中,主進程、子進程間要共享變量的話,需要創建能在進程間共享的變量;
#coding=utf-8
from multiprocessing import Process,Value,Array
def f(n,a):
n.value = 3.1415
for i in range(len(a)):
a[i] = -a[i]
if __name__ == "__main__":
num = Value("d",0.0)#創建一個進程間共享的數字類型,默認值爲0
arr = Array("i",range(10))#創建一個進程間共享的數組類型,初始值爲range[10]
p = Process(target=f,args=(num,arr))
p.start()
p.join()
print(num.value)#打印共享變量num的值
print(arr[:])
共享變量、加鎖
#coding=utf-8
from multiprocessing import Process,Value,Lock
import time
class Counter(object):
def __init__(self,initval = 0):
self.val = Value("i",initval)
self.lock = Lock()
def increment(self):
with self.lock:#加鎖,保證同一時刻只能有一個進程在操作共享變量
self.val.value += 1
def value(self):
with self.lock:
return self.val.value
def func(counter):
for i in range(50):
time.sleep(0.01)
counter.increment()
if __name__ == "__main__":
counter = Counter(0)
procs = [Process(target=func,args=(counter,)) for i in range(10)]
for p in procs:
p.start()
for p in procs:
p.join()
print(counter.value())
多進程間共享字符串變量
#coding=utf-8
from multiprocessing import Process,Value,Manager
from ctypes import c_char_p
def greet(shareStr):
shareStr.value = shareStr.value + ",World!"
if __name__ == "__main__":
manager = Manager()
shareStr = manager.Value(c_char_p,"Hello")#主進程的變量
process = Process(target = greet,args=(shareStr,))#子進程共享主進程的字符串變量
process.start()
process.join()
print(shareStr.value)
多進程間共享不同類型的數據結構對象
#coding=utf-8
from multiprocessing import Process,Manager
def f(shareDict,shareList):
shareDict[1] = "1"
shareDict["2"] = 2
shareDict[0.25] = None
shareList.reverse()
if __name__ == "__main__":
manager = Manager()
shareDict = manager.dict() #創建共享的字典
shareList = manager.list(range(10)) #創建共享的列表
p = Process(target=f,args=(shareDict,shareList))
p.start()
p.join()
print(shareDict)
print(shareList)
Manager()函數返回一個管理對象,它控制了一個服務端進程,用來保持Python對象,並允許其它進程使用代理來管理這些對象。Manager()返回的管理者,支持類型包括,list, dict, Namespace, Lock,RLock, Semaphore, BoundedSemaphore,Condition, Event, Queue, Value and Array。managers比使用共享內存對象更靈活,因爲它支持任意對象類型。
同樣的,單個的manager可以通過網絡在不同機器上進程間共享。但是,會比共享內存慢。
進程間共享實例對象
#coding=utf-8
from multiprocessing import Pool,Value,Lock
from multiprocessing.managers import BaseManager
import os,time
class MyManager(BaseManager):
pass
def Manager():
m = MyManager()
m.start()
return m
class Counter(object):
def __init__(self,initval=0):
self.val = Value("i",initval)
self.lock = Lock()
def increment(self):
with self.lock:
self.val.value += 1
def value(self):
with self.lock:
return self.val.value
MyManager.register("Counter",Counter)
def long_time_task(name,counter):
time.sleep(0.2)
print("Run task %s (%s)...\n" %(name,os.getpid()))
start = time.time()
for i in range(50):
time.sleep(0.01)
counter.increment()
end = time.time()
print("Task %s runs %0.2f seconds." %(name,(end-start)))
if __name__ == "__main__":
manager = Manager()
counter = manager.Counter(0)
print("Parent process %s." %os.getpid())
p = Pool()
for i in range(5):
p.apply_async(long_time_task,args=(str(i),counter))
print("Waiting for all subprocesses done...")
p.close()
p.join()
print("All subprocesses done.")
print(counter.value())
進程日誌
#encoding=utf-8
import multiprocessing
import logging
import sys
def worker():
print("I am working...")
sys.stdout.flush()
if __name__ == "__main__":
#設置日誌輸出到控制檯
multiprocessing.log_to_stderr()
#獲取日誌對象
logger = multiprocessing.get_logger()
#設置日誌的級別
logger.setLevel(logging.INFO)
p = multiprocessing.Process(target=worker)
p.start()
p.join()
守護進程
守護進程就是不阻擋主進程退出,會隨着主進程退出而退出,如果要等待守護進程退出,需要加上join函數。
#encoding=utf-8
import multiprocessing
import logging
import sys,time
def daemon():
p = multiprocessing.current_process()
print("Starting: ",p.name,p.pid)
#緩衝區數據顯示到終端
sys.stdout.flush()
print("Exiting: ",p.name,p.pid)
sys.stdout.flush()
def non_daemon():
p = multiprocessing.current_process()
print("Starting:",p.name,p.pid)
sys.stdout.flush()
print("Exiting: ",p.name,p.pid)
sys.stdout.flush()
if __name__ == "__main__":
#設置日誌輸出到控制檯
multiprocessing.log_to_stderr()
#獲取日誌對象
logger = multiprocessing.get_logger()
#設置日誌級別
logger.setLevel(logging.DEBUG)
d = multiprocessing.Process(name="daemon",target=daemon)
#設置進程d爲守護進程
d.daemon = True
n = multiprocessing.Process(name="nondaemon",target=daemon)
#設置進程n爲非守護進程
n.daemon = False
d.start()
#time.sleep(1)#不要sleep
n.start()
#d.join()
#n.join()
print("d.is_alive()",d.is_alive())
print("n.is_alive()",n.is_alive())
print("main Process end! ")
主進程首先執行完成,守護進程隨着主進程退出直接退出了,根據沒有執行守護進程的內容
import subprocess
subprocess.run(args=['ls','-al'])
>>> subprocess.run(["dir","test"],shell = True,cwd="e:\\")
>>> subprocess.Popen(["dir","test"],shell = True,cwd="e:\\")
<subprocess.Popen object at 0x00000000025DA7F0>
subprocess.run(["ls","-al"],shell=True,cwd="/home")
subprocess模塊
從Python2.4版本以後,可以使用subprocess這個模塊來產生子進程,並連接到子進程的標準輸入/輸出/錯誤中去,還可以得到子進程的返回值。看一下官方的解釋:
DESCRIPTION
This module allows you to spawn processes, connect to their
input/output/error pipes, and obtain their return codes.
即允許你去創建一個新的進程讓其執行另外的程序,並與它進行通信,獲取標準的輸入、標準輸出、標準錯誤以及返回碼等。另外subprocess還提供了一些管理標準流(standard stream)和管道(pipe)的工具,從而在進程間使用文本通信。
創建subprocess進程
subprocess模塊用subprocess.Popen類來創建進程,並與進程進行復雜的交互。其構造函
數如下:
__init__(self, args, bufsize=0, executable=None, stdin=None, stdout=None,
stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None,
universal_newlines=False, startupinfo=None, creationflags=0)
主要參數說明:
✓ args:
args should be a string, or a sequence of program arguments.也就是說必須是一個字符
串或者序列類型(如:字符串、list、元組),用於指定進程的可執行文件及其參數。如果是一個序列類型參數,則序列的第一個元素通常都必須是一個可執行文件的路徑。當然也可以使用executeable參數來指定可執行文件的路徑。
✓ bufsize:
如果指定了bufsize參數作用就和內建函數open()一樣:0表示不緩衝,1表示行緩衝,其他正
數表示近似的緩衝區字節數,負數表示使用系統默認值。默認是0。
✓ stdin,stdout,stderr:
分別表示程序的標準輸入、標準輸出、標準錯誤。有效的值可以是PIPE,存在的文件描述符
,存在的文件對象或None,如果爲None需從父進程繼承過來;stdout可以是PIPE,表示對
子進程創建一個管道;stderr可以是STDOUT,表示標準錯誤數據應該從應用程序中捕獲並
作爲標準輸出流stdout的文件句柄。
✓ shell:
如果這個參數被設置爲True,程序將通過shell來執行。
✓ env:
它描述的是子進程的環境變量。如果爲None,子進程的環境變量將從父進程繼承而來。
創建Popen類的實例對象:
res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
cmd:向子進程傳入需要執行的shell命令,如:ls –al
subprocess.PIPE:在創建Popen對象時,subprocess.PIPE可以初始化爲stdin, stdout或stderr的參數,表示與子進程通信的標準輸入流,標準輸出流以及標準錯誤,將多個子進程的輸入和輸出連接在一起,構成管道(pipe)。
subprocess.STDOUT:作爲Popen對象的stderr的參數,表示將標準錯誤通過標準輸出流輸出。
示例1:
在目錄e:\\test創建一個文件夾hhq2
>>> a = subprocess.Popen("mkdir hhq2",shell=True,cwd="e:\\test")#命令爲一個字符串
>>> a
<subprocess.Popen object at 0x000000000280A080>
>>> a = subprocess.Popen(["mkdir", "hhq4"],shell=True,cwd="e:\\test")#命令爲一個裏列表
>>> a
<subprocess.Popen object at 0x000000000255BFD0>
>>> print(a)
<subprocess.Popen object at 0x000000000255BFD0>
示例2:
#encoding=utf-8
import subprocess
#得到子進程obj,執行python命令,標準輸入、輸出都是管道
obj = subprocess.Popen
(["python"],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
#從標準輸入寫入內容
obj.stdin.write(b"print(1);")#需要寫入bytes類型,
obj.stdin.write(b"print(2);")
obj.stdin.write(b"print(3);")
obj.stdin.write(b"print(4);")
obj.stdin.close()
#讀出標準輸出的內容
cmd_out = obj.stdout.read()
obj.stdout.close()
cmd_error = obj.stderr.read()
obj.stderr.close()
print(cmd_out)
print(cmd_error)
示例3:
#encoding=utf-8
import subprocess
obj = subprocess.Popen
(["python"],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
obj.stdin.write(b"print(1)\n")
obj.stdin.write(b"print(2)\n")
obj.stdin.write(b"print(3)\n")
obj.stdin.write(b"print(4)\n")
out_info,out_error = obj.communicate()#返回一個元組,返回標準輸出和錯誤輸出
print(out_info)
print(out_error)
示例4,將一個子進程的輸出,作爲另一個子進程的輸入:
#encoding=utf-8
import subprocess
child1 = subprocess.Popen(["ls","-al"],stdout=subprocess.PIPE)
child2 = subprocess.Popen(["wc","-l"],stdin=child1.stdout,stdout=subprocess.PIPE)
out = child2.communicate()#返回的是一個元組
print(out)
Subprocess進程通信實例
打開一個只有ip地址的文本文件,讀取其中的ip或域名,然後進行ping操作,
並將ping結果寫入ping.txt文件中。ip.txt文件內容如下:
www.baidu.com
www.taobao.com
123.45.5.34
127.0.0.1
#encoding=utf-8
import subprocess
import os
class Shell(object):
def runCmd(self,cmd):
res = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
#獲取子進程的標準輸出、標準錯誤信息
sout,serr = res.communicate()
#返回進程指定的結果碼、輸出信息、錯誤信息,子進程的編號
return res.returncode,sout,serr,res.pid
shell = Shell()
fp = open("e:\\ip.txt",encoding="utf-8")
ipList = fp.readlines()
fp.close()
fp = open("e:\\ping.txt","a",encoding="utf-8")
print(ipList)
for ip in ipList:
ip = ip.strip()
result = shell.runCmd("ping " + ip)
if result[0] == 0:
w = ip + ": 0"
else:
w = ip + ": 1"
fp.write(w + "\n")
print(result[1].decode("gbk"))
fp.close()
標準輸入、輸出、錯誤輸出
>>> import sys
>>> a = sys.stdin.readline()
dddd
>>> a
'dddd\n'
>>> sys.stdout.write("bb")
bb2#輸出字符個數
>>> sys.stderr.write("ddd")
3
ddd>>>
多進程(三)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.