多進程介紹

實驗發現不涉及IO輸入的多線程,串行運行在老的(新解釋器縮短差距)解釋器有時候比多線程快,這是什麼緣由?python

GIL:全局解釋鎖(這玩意跟python語言無關,跟解釋種類有關,只對CPython解釋器有用,可是這種站主導市場)
       由於有GIL,因此同一時刻,只有一個線程被一個CPU執行
        多核對於Python多線程用不上,完了!!!
        多進程能夠用上多核,可是進程的開銷大!!安全

總結:多線程用不上多核(計算密集型任務時,不如串行;IO密集型任務它仍是不錯的),多進程太多時開銷大,因此用多進程+協程。數據結構

 1 #!/usr/bin/env python
 2 #-*-coding:utf-8 -*-
 3 
 4 '''
 5 python 大都編輯器的單一進程中的多線程是沒法用多核的,由於GIL;可是多進程能夠,因此多進程模塊multiprocessing
 6 線程的調用兩種方法,一是直接用treading模塊,二是繼承這個模塊。進程類似(守護進程deamon有點區別)
 7 若是有4個CPU,開4個進程,就能夠實現真的並行,而不是併發
 8 '''
 9 
10 import time, multiprocessing
11 import threading
12 
13 def f(name):
14     time.sleep(1)
15     print("hello",name,time.ctime())
16 
17 if __name__ == '__main__':
18     plist = []
19     for i in range(2):
20         p = multiprocessing.Process(target=f,args=("wan",))
21         # p = threading.Thread(target=f,args=('wan',))
22         plist.append(p)
23         p.start()
24 
25     for i in plist:
26         i.join()
27 
28     print("ending........")
 1 from multiprocessing import Process
 2 import os
 3 import time
 4 
 5 
 6 def info(title):
 7     print("title:", title)
 8     print('parent process:', os.getppid())  # 每一個進程有一個ID號,getppid是獲得父進程id
 9     print('process id:', os.getpid())       # getpid是獲得本身當前運行的id
10 
11 def f(name):
12 
13     info('function f')
14     print('hello', name)
15 
16 
17 if __name__ == '__main__':
18 
19     info('main process line')
20 
21     time.sleep(1)
22     print("------------------")
23     p = Process(target=info, args=('yuan',))     # 這裏的父進程的ID號應該是等於上面info調用的後等到的process id 同樣
24     p.start()
25     p.join()
View Code

 介紹一下multiprocessing.Process類:多線程

        is_alive() 返回進程是否運行併發

        terminate () 無論任務是否完成,當即中止工做進程app

進程之間的通信,由於一個進程中的多個線程是在同一塊進程的內存空間中,他們數據共享,很簡單。可是進程之間的內存空間不一樣,要解決進程之間的通信,以及共享數據問題(我改了數據,你拿到的應該是我改後的)。進程中涉及大量的數據拷貝,因此資源消耗大異步

下面講進程通信:隊列,管道(前兩個只是完成通信功能,沒有實現數據共享),Managers(可實現數據共享)

隊列:

         說隊列前,先介紹一下列表這種數據結構,看看它在線程中的缺陷,線程不安全。async

 1 import threading,time
 2 
 3 li = [1, 2, 3, 4, 5]
 4 
 5 def pri():    # 每一個線程刪除列表中的最後一個元素
 6     while li:
 7         a = li[-1]
 8         print(a)
 9         time.sleep(1)
10         li.remove(a)
11 
12 t1 = threading.Thread(target=pri)
13 t2 = threading.Thread(target=pri)
14 
15 t1.start()
16 t2.start()
17 
18 t1.join()
19 t2.join()
20 print("end.............")
View Code

       用了隊列,剛纔列表在線程中的問題就不存在了。編輯器

 1 import queue
 2 
 3 q = queue.Queue(3) # 先進先出,後進先出是.LifeoQueue,還能夠按級別出.PriorityQueue
 4 
 5 q.put(12)          # put 與 get還有一些其餘的參數
 6 q.put("hello")
 7 q.put({'name':"wan"})
 8 
 9 print(q.qsize())       # 隊列中實際有多少值
10 print(q.empty())       # 是否爲空
11 print(q.full())        # 是否爲滿
12 
13 while 1:
14     data = q.get()
15     print(data)
16     print("--------------")
View Code

      一樣,用隊列進行進程中的通信以下:ide

 1 import multiprocessing
 2 # import queue
 3 
 4 
 5 def foo(q):
 6 
 7     q.put(123)
 8     q.put("wan")
 9 
10 if __name__ == '__main__':
11     q = multiprocessing.Queue()               # 進程中有本身的隊列
12     plist = []
13     for i in range(3):
14         p = multiprocessing.Process(target=foo,args=(q,))
15         # 這裏必須把參數q傳進去,由於它不像線程那樣共享,在主線程創建q,只是主進程內存空間中,子進程中隊列都沒定義
16         plist.append(p)
17         p.start()
18     print(q.get())
19     print(q.get())
20     print(q.get())
21     print(q.get())
multiprocessing.Queue

管道通信:

 1 from multiprocessing import Process, Pipe
 2 def f(conn):
 3     conn.send([12, {"name":"yuan"}, 'hello'])       # 子進程發
 4     response=conn.recv()                            # 子進程收
 5     print("response",response)
 6     conn.close()
 7     # print("q_ID2:",id(conn))
 8 
 9 if __name__ == '__main__':
10 
11     parent_conn, child_conn = Pipe()                 # 雙向管道,相似電話的兩頭,一頭給子進程,一頭給主進程
12 
13     # print("q_ID1:",id(child_conn))
14     p = Process(target=f, args=(child_conn,))
15     p.start()
16 
17     print(parent_conn.recv())   # 父進程(主進程)接收到的
18     parent_conn.send("兒子你好!") # 父進程發
19     p.join()
multiprocessing.Pipe

Manager:

 1 from multiprocessing import Process, Manager
 2 
 3 def f(d, l,n):
 4 
 5     d[n] = '1'    #{0:"1"}
 6     d['2'] = 2    #{0:"1","2":2}
 7 
 8     l.append(n)    #[0,1,2,3,4,   0,1,2,3,4,5,6,7,8,9]
 9     #print(l)
10 
11 
12 if __name__ == '__main__':
13 
14     with Manager() as manager:        # 相似用with打開文件,省略關的功能
15 
16         d = manager.dict()            #{}
17 
18         l = manager.list(range(5))     #[0,1,2,3,4]
19 
20 
21         p_list = []
22 
23         for i in range(10):
24             p = Process(target=f, args=(d,l,i))
25             p.start()
26             p_list.append(p)
27 
28         for res in p_list:
29             res.join()
30 
31         print(d)
32         print(l)
View Code

 

#!/usr/bin/env python
#-*-coding:utf-8 -*-

'''
線程有同步(同步鎖):由於它們共享數據,可能會出錯;
進程數據不共享,可是也共享一些資源(好比屏幕(進程1與進程2可能同一時刻想同時輸出在屏幕上,也就是出現串行錯誤))
所以,進程也須要同步multiprocessing.Lock

'''

from multiprocessing import Process,Lock

def f(l,i):
    l.acquire()
    print('hello world %s'%i)
    l.release()

if __name__ == '__main__':
    lock = Lock()

    for num in range(12):
        Process(target=f,args=(lock, num)).start()

    print('ending.............')

 

 1 '''
 2 進程池:控制開進程的最大量 multiprocessing.Pool
 3 進程池兩個方法:apply同步 , apply_async異步
 4 
 5 回調函數:就是某個動做或者某個函數執行成功後再去執行的函數
 6         感受回調函數能夠把內容寫在它跟的函數後面,這樣基本就等同了
 7         可是callback是在主進程下面調用的
 8 '''
 9 
10 from multiprocessing import Process,Pool
11 import time,os
12 
13 def f(i):
14     time.sleep(3)
15     print(i)
16     print("son pid: ",os.getpid())
17     return 'hello %s'%i
18 
19 def Bar(arg):                  # 回調函數必須至少有一個參數,就是它跟的函數的返回值
20     print(arg)
21     print('callback pid: ',os.getpid())
22 
23 if __name__ == '__main__':
24     pool = Pool(5)
25     print("main pid:  ",os.getpid())
26     for i in range(100):
27         pool.apply_async(func=f,args=(i,),callback=Bar)       # callback是回調函數名字,它在每一個子進程執行完後執行
28 
29     pool.close()
30     pool.join()             # 注意這個pool.close與pool.join的順序是固定的
相關文章
相關標籤/搜索