网络编程中的锁与队列
進程的其他方法:
1 import os,time 2 from multiprocessing import Process 3 def f1(): 4 print('子進程的pid',os.getpid()) #查看當前子進程的id 5 print('子進程的父進程的pid',os.getppid()) 6 print(123) 7 8 def f2(): 9 print('子進程的pid',) 10 print('父進程的pid',) 11 print(123) 12 13 if __name__ == '__main__': 14 p1 = Process(target=f1,name='洪七') 15 p2 = Process(target=f2) 16 p1.start() 17 p2.start() 18 print(p1.name) #查看當前子進程的name,可以進行賦值更改name 19 print('子進程的pid',p1.pid) #查看當前子進程的id 20 print('父進程的pid',os.getpid()) 21 def f3(): 22 time.sleep(5) 23 print('子進程2') 24 if __name__ == '__main__': 25 p = Process(target=f3,) 26 p.start() 27 print(p.is_alive()) #判斷子進程是否還活著,是否還在運行 28 p.terminate() #給操作系統(tǒng)發(fā)送一個結束進程的信號 29 print(p.is_alive())驗證進程之間是空間隔離的:
?
1 from multiprocessing import Process 2 3 num = 100 4 5 def f1(): 6 global num 7 num = 3 8 print('子進程中的num',num) 9 10 print('>>>>>',num) 11 if __name__ == '__main__': 12 p = Process(target=f1,) 13 p.start() 14 p.join() 15 print('主進程中的num',num)?
僵尸進程與孤兒進程(簡單了解 一下)
1 一:僵尸進程(有害) 2 僵尸進程:一個進程使用fork創(chuàng)建子進程,如果子進程退出,而父進程并沒有調用wait或waitpid獲取子進程的狀態(tài)信息,那么子進程的進程描述符仍然保存在系統(tǒng)中。這種進程稱之為僵死進程。詳解如下 3 4 我們知道在unix/linux中,正常情況下子進程是通過父進程創(chuàng)建的,子進程在創(chuàng)建新的進程。子進程的結束和父進程的運行是一個異步過程,即父進程永遠無法預測子進程到底什么時候結束,如果子進程一結束就立刻回收其全部資源,那么在父進程內將無法獲取子進程的狀態(tài)信息。 5 6 因此,UNⅨ提供了一種機制可以保證父進程可以在任意時刻獲取子進程結束時的狀態(tài)信息: 7 1、在每個進程退出的時候,內核釋放該進程所有的資源,包括打開的文件,占用的內存等。但是仍然為其保留一定的信息(包括進程號the process ID,退出狀態(tài)the termination status of the process,運行時間the amount of CPU time taken by the process等) 8 2、直到父進程通過wait / waitpid來取時才釋放. 但這樣就導致了問題,如果進程不調用wait / waitpid的話,那么保留的那段信息就不會釋放,其進程號就會一直被占用,但是系統(tǒng)所能使用的進程號是有限的,如果大量的產生僵死進程,將因為沒有可用的進程號而導致系統(tǒng)不能產生新的進程. 此即為僵尸進程的危害,應當避免。 9 10 任何一個子進程(init除外)在exit()之后,并非馬上就消失掉,而是留下一個稱為僵尸進程(Zombie)的數據結構,等待父進程處理。這是每個子進程在結束時都要經過的階段。如果子進程在exit()之后,父進程沒有來得及處理,這時用ps命令就能看到子進程的狀態(tài)是“Z”。如果父進程能及時 處理,可能用ps命令就來不及看到子進程的僵尸狀態(tài),但這并不等于子進程不經過僵尸狀態(tài)。 如果父進程在子進程結束之前退出,則子進程將由init接管。init將會以父進程的身份對僵尸狀態(tài)的子進程進行處理。 11 12 二:孤兒進程(無害) 13 14 孤兒進程:一個父進程退出,而它的一個或多個子進程還在運行,那么那些子進程將成為孤兒進程。孤兒進程將被init進程(進程號為1)所收養(yǎng),并由init進程對它們完成狀態(tài)收集工作。 15 16 孤兒進程是沒有父進程的進程,孤兒進程這個重任就落到了init進程身上,init進程就好像是一個民政局,專門負責處理孤兒進程的善后工作。每當出現一個孤兒進程的時候,內核就把孤 兒進程的父進程設置為init,而init進程會循環(huán)地wait()它的已經退出的子進程。這樣,當一個孤兒進程凄涼地結束了其生命周期的時候,init進程就會代表黨和政府出面處理它的一切善后工作。因此孤兒進程并不會有什么危害。 17 18 我們來測試一下(創(chuàng)建完子進程后,主進程所在的這個腳本就退出了,當父進程先于子進程結束時,子進程會被init收養(yǎng),成為孤兒進程,而非僵尸進程),文件內容 19 20 import os 21 import sys 22 import time 23 24 pid = os.getpid() 25 ppid = os.getppid() 26 print 'im father', 'pid', pid, 'ppid', ppid 27 pid = os.fork() 28 #執(zhí)行pid=os.fork()則會生成一個子進程 29 #返回值pid有兩種值: 30 # 如果返回的pid值為0,表示在子進程當中 31 # 如果返回的pid值>0,表示在父進程當中 32 if pid > 0: 33 print 'father died..' 34 sys.exit(0) 35 36 # 保證主線程退出完畢 37 time.sleep(1) 38 print 'im child', os.getpid(), os.getppid() 39 40 執(zhí)行文件,輸出結果: 41 im father pid 32515 ppid 32015 42 father died.. 43 im child 32516 1 44 45 看,子進程已經被pid為1的init進程接收了,所以僵尸進程在這種情況下是不存在的,存在只有孤兒進程而已,孤兒進程聲明周期結束自然會被init來銷毀。 46 47 48 三:僵尸進程危害場景: 49 50 例如有個進程,它定期的產 生一個子進程,這個子進程需要做的事情很少,做完它該做的事情之后就退出了,因此這個子進程的生命周期很短,但是,父進程只管生成新的子進程,至于子進程 退出之后的事情,則一概不聞不問,這樣,系統(tǒng)運行上一段時間之后,系統(tǒng)中就會存在很多的僵死進程,倘若用ps命令查看的話,就會看到很多狀態(tài)為Z的進程。 嚴格地來說,僵死進程并不是問題的根源,罪魁禍首是產生出大量僵死進程的那個父進程。因此,當我們尋求如何消滅系統(tǒng)中大量的僵死進程時,答案就是把產生大 量僵死進程的那個元兇槍斃掉(也就是通過kill發(fā)送SIGTERM或者SIGKILL信號啦)。槍斃了元兇進程之后,它產生的僵死進程就變成了孤兒進 程,這些孤兒進程會被init進程接管,init進程會wait()這些孤兒進程,釋放它們占用的系統(tǒng)進程表中的資源,這樣,這些已經僵死的孤兒進程 就能瞑目而去了。 51 52 四:測試 53 #1、產生僵尸進程的程序test.py內容如下 54 55 #coding:utf-8 56 from multiprocessing import Process 57 import time,os 58 59 def run(): 60 print('子',os.getpid()) 61 62 if __name__ == '__main__': 63 p=Process(target=run) 64 p.start() 65 66 print('主',os.getpid()) 67 time.sleep(1000) 68 69 70 #2、在unix或linux系統(tǒng)上執(zhí)行 71 [root@vm172-31-0-19 ~]# python3 test.py & 72 [1] 18652 73 [root@vm172-31-0-19 ~]# 主 18652 74 子 18653 75 76 [root@vm172-31-0-19 ~]# ps aux |grep Z 77 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND 78 root 18653 0.0 0.0 0 0 pts/0 Z 20:02 0:00 [python3] <defunct> #出現僵尸進程 79 root 18656 0.0 0.0 112648 952 pts/0 S+ 20:02 0:00 grep --color=auto Z 80 81 [root@vm172-31-0-19 ~]# top #執(zhí)行top命令發(fā)現1zombie 82 top - 20:03:42 up 31 min, 3 users, load average: 0.01, 0.06, 0.12 83 Tasks: 93 total, 2 running, 90 sleeping, 0 stopped, 1 zombie 84 %Cpu(s): 0.0 us, 0.3 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st 85 KiB Mem : 1016884 total, 97184 free, 70848 used, 848852 buff/cache 86 KiB Swap: 0 total, 0 free, 0 used. 782540 avail Mem 87 88 PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 89 root 20 0 29788 1256 988 S 0.3 0.1 0:01.50 elfin 90 91 92 #3、 93 等待父進程正常結束后會調用wait/waitpid去回收僵尸進程 94 但如果父進程是一個死循環(huán),永遠不會結束,那么該僵尸進程就會一直存在,僵尸進程過多,就是有害的 95 解決方法一:殺死父進程 96 解決方法二:對開啟的子進程應該記得使用join,join會回收僵尸進程 97 參考python2源碼注釋 98 class Process(object): 99 def join(self, timeout=None): 100 ''' 101 Wait until child process terminates 102 ''' 103 assert self._parent_pid == os.getpid(), 'can only join a child process' 104 assert self._popen is not None, 'can only join a started process' 105 res = self._popen.wait(timeout) 106 if res is not None: 107 _current_process._children.discard(self) 108 109 join方法中調用了wait,告訴系統(tǒng)釋放僵尸進程。discard為從自己的children中剔除?
守護進程:(**)
主進程創(chuàng)建守護進程
其一:守護進程會在主進程代碼執(zhí)行結束后就終止
其二:守護進程內無法再開啟子進程,否則拋出異常:AssertionError: daemonic processes are not allowed to have children
注意:進程之間是互相獨立的,主進程代碼運行結束,守護進程隨即終止
守護進程代碼展示:
1 import time 2 from multiprocessing import Process 3 4 def f1(): 5 time.sleep(3) 6 print('xxxx') 7 8 def f2(): 9 time.sleep(5) 10 print('普通子進程的代碼') 11 if __name__ == '__main__': 12 p = Process(target=f1,) 13 p.daemon = True #將該進程設置為守護進程,必須寫在start之前,意思如果我的主進程代碼運行結束了,你這個子進程不管運行到什么地方,都直接結束 14 p.start() 15 16 #開啟一個普通的子進程來驗證一下守護進程的結束只和主進程的代碼運行結束有關系,而整個程序的結束需要主進程和普通的子進程的代碼都運行結束才結束 17 p2 = Process(target=f2,) 18 p2.start() 19 #等待2號普通進程的結束,才繼續(xù)執(zhí)行下面主進程中的代碼 20 # p2.join() 21 #守護進程會跟跟著父進程的代碼運行結束,就結束 22 print('主進程結束')進程鎖(同步鎖/互斥鎖)? *****
進程之間數據不共享,但是共享同一套文件系統(tǒng),所以訪問同一個文件,或同一個打印終端,是沒有問題的,而共享帶來的是競爭,競爭帶來的結果就是錯亂,如何控制,就是加鎖處理。
簡單的進程鎖代碼展示
1 # 互斥鎖/進程鎖/同步鎖 2 # import json 3 import time 4 from multiprocessing import Process,Lock 5 6 def show_t(i): 7 8 with open('ticket','r',encoding='utf-8') as f: 9 ticket_data = f.read() 10 # print(ticket_data) 11 t_data = eval(ticket_data) 12 # print(t_data,type(t_data)) 13 print('%s查詢剩余票數為%s'%(i,t_data['count'])) 14 15 def get_t(i,l1): 16 l1.acquire() 17 with open('ticket', 'r', encoding='utf-8') as f: 18 ticket_data = f.read() 19 # print(ticket_data) 20 t_data = eval(ticket_data) 21 # print(t_data,type(t_data)) 22 # print('%s查詢剩余票數為%s' % (i, t_data['count'])) 23 if t_data['count'] > 0: 24 t_data['count'] -= 1 25 print('%s搶票成功'%i) 26 time.sleep(0.2) 27 with open('ticket', 'w') as f: 28 f.write(str(t_data)) 29 30 else: 31 print('沒票了!!!') 32 l1.release() 33 34 if __name__ == '__main__': 35 l1 = Lock() 36 for i in range(10): 37 p1 = Process(target=show_t,args=(i,)) 38 p1.start() 39 for i in range(10): 40 p2 = Process(target=get_t,args=(i,l1) ) 41 p2.start()數據共享:
1 import time 2 from multiprocessing import Process,Manager,Lock 3 4 def f1(m_d,l2): 5 # m_d['num'] -= 1 # 6 with l2: 7 # l2.acquire() 8 tmp = m_d['num'] 9 tmp -= 1 10 time.sleep(0.1) 11 m_d['num'] = tmp 12 # l2.release() 13 14 if __name__ == '__main__': 15 m = Manager() 16 l2 = Lock() 17 m_d = m.dict({'num':100}) 18 p_list = [] 19 for i in range(10): 20 p = Process(target=f1,args=(m_d,l2)) 21 p.start() 22 p_list.append(p) 23 24 [pp.join() for pp in p_list] 25 26 print(m_d['num'])for?循環(huán)創(chuàng)建多進程:
1 import time 2 from multiprocessing import Process 3 4 5 def f1(): 6 time.sleep(0.5) 7 print('xxx') 8 9 if __name__ == '__main__': 10 p_list = [] 11 #for循環(huán)創(chuàng)建子進程,并且完成主進程等待所有子進程執(zhí)行結束,才繼續(xù)執(zhí)行 12 for i in range(10): 13 p = Process(target=f1,) 14 p.start() 15 p_list.append(p) 16 p.join() 17 # for pp in p_list: 18 # pp.join() 19 20 print('主進程結束')隊列(Queue):
進程彼此之間互相隔離,要實現進程間通信(IPC),multiprocessing模塊支持兩種形式:隊列和管道,這兩種方式都是使用消息傳遞的。隊列就像一個特殊的列表,但是可以設置固定長度,并且從前面插入數據,從后面取出數據,先進先出。
Queue([maxsize]) 創(chuàng)建共享的進程隊列。參數 :maxsize是隊列中允許的最大項數。如果省略此參數,則無大小限制。
底層隊列使用管道和鎖實現。
基于隊列的進程通信: 1 from multiprocessing import Process,Queue 2 3 def f1(q): 4 q.put('約嗎?') 5 6 if __name__ == '__main__': 7 q = Queue(3) 8 9 p = Process(target=f1,args=(q,)) 10 p.start() 11 12 son_p_msg = q.get() 13 14 print('來自子進程的消息:',son_p_msg)
利用隊列實現一個生產消費模型:
1 import time 2 from multiprocessing import Process,JoinableQueue 3 #生產者 4 def producer(q): 5 for i in range(10): 6 time.sleep(0.2) 7 s = '大包子%s號'%i 8 print(s+'新鮮出爐,拿去用') 9 q.put(s) 10 q.join() #就等著task_done()信號的數量,和我put進去的數量相同時,才繼續(xù)執(zhí)行 11 print('所有的任務都被處理了,繼續(xù)潛行吧騷年們') 12 13 def consumer(q): 14 while 1: 15 time.sleep(0.5) 16 baozi = q.get() 17 18 print(baozi+'被吃了') 19 q.task_done() #給隊列發(fā)送一個取出的這個任務已經處理完畢的信號 20 21 if __name__ == '__main__': 22 # q = Queue(30) 23 q = JoinableQueue(30) #同樣是一個長度為30的隊列 24 25 pro_p = Process(target=producer,args=(q,)) 26 con_p = Process(target=consumer,args=(q,)) 27 pro_p.start() 28 con_p.daemon = True 29 con_p.start() 30 31 32 pro_p.join() 33 print('主進程結束')?
進程隊列? ***** Queue()
Q = Queue(5)
Q.put()? #滿了會等待
Q.get()? #沒有數據了會等待
Q.qsize()
Q.empty()? 不可靠
Q.full()不可靠?
Q.get_nowait()? #不等待,但是報錯
Q.put_nowait()?? #不等待,但是報錯
轉載于:https://www.cnblogs.com/Godisgirl/p/10246522.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的网络编程中的锁与队列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【J2EE】第四章 SpringMVC
- 下一篇: Docker for Linux 安装