多進程(三)

進程同步( 使用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>>>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章