python學習筆記——多線程 / 多進程

threading 多線程庫 (IO操作使用)

格式:

t1 = threading.Thread(target=要執行的函數名,args=(該函數的參數))

一般方法:

setDaemon(True) 守護線程,默認參數爲False,參數爲True時開啓守護,需防止在start()之前

start() #啓動線程

getName() #獲取線程名字

join() #主線程到達join停止,等待子線程執行。參數爲‘超時時間’。如設置5,則等待5秒

直接調用方式

import threading
import time

def sayhi(num):
    print('Thread number is :%s'%num)
    time.sleep(3)

if __name__ == '__main__':

    t1 = threading.Thread(target=sayhi,args=(1,)) #第一個參數是需要調用的方法名,第二個參數是調用方法的參數
    t2 = threading.Thread(target=sayhi,args=(2,))
    t1.start() #啓動線程
    t2.start()

    print(t1.getName())#獲取線程名
    print(t2.getName())
    t2.join() #主線程遇到join後,會等待這個子線程執行完畢。也可以放參數,比如5,就是5秒
    print('__main__')

正常情況下,主線程啓動了子線程之後,就和子線程沒有了關係,不等子線程執行完畢就會繼續執行下去

繼承式調用:

run() 

import threading
import time

class MyThread(threading.Thread):##繼承多線程

    def __init__(self,num):#重寫構造方法,同時繼承父類構造方法
        threading.Thread.__init__(self)#繼承父類的構造方法
        self.num = num

    def run(self):#定義每個線程要運行的函數,必須有這個方法,方法名只能叫run
        print('running on number :%s' %self.num)
        time.sleep(3)

if __name__ == '__main__':

    t1 = MyThread(1)
    t2 = MyThread(2)
    t1.start()
    t2.start()

Daemon()守護進程

import threading
import time

#設置兩層多線程
def run(num):#子線程調用的run函數
    print('%s-----running-----\n' %num)
    time.sleep(2)
    print('----done-----')

def main():#main方法開啓5個子線程
    for i in range(5):
        t = threading.Thread(target=run,args=(i,))
        t.start()
        print('start thread', t.getName())

m = threading.Thread(target=main,args=())#主線程調用main方法
m.setDaemon(True) #主線程設置成守護縣城,它退出時,其他子線程會同時退出
m.start()
m.join(10)#設置主線程等待,join只能放在start之後.設置參數後就不阻塞了。??

print('------main done ------')
 
Lock 線程鎖  & python GIL

python GIL 是爲了防止底層C的原生線程產生不安全行爲,不是防止python這一層的多線程的。所以還需要一把鎖保證上層數據的多線程安全。

import threading
import time


def addNum(): #多線程共同操作的函數
    global num #在每個線程中都獲得這個全局變量
    print('----get num:',num)
    time.sleep(1)
    lock.acquire()#獲取一把鎖(鎖的意思就是把這塊代碼塊變單線程串行運行了)
    num -=1 #對此公共變量進行-1操作
    lock.release()#用完後釋放鎖

lock = threading.Lock()#創建線程鎖實例
num = 100 #設置一個共享變量
thread_list = []
for i in range(100):
    t = threading.Thread(target=addNum,args=())
    t.start()
    thread_list.append(t)

for i in thread_list: #等待所有線程執行完畢
    i.join()

print('final num:',num)
Lock & RLock 遞歸鎖

遞歸鎖,就是一個大鎖中還要再包含子鎖

第一把鎖釋放之前要獲取第二把鎖

queue隊列

class queue.Queue(maxsize=0)  先入先出

import queue

q = queue.Queue(maxsize=3)#定義長度
q.put([1,2,3])#把數據放入隊列,可以放入任何數據,包括實例.
data = q.get_nowait() #取出數據.
print(type(data))
print(q.full())#判斷是否滿了,這裏會返回False
print(q.empty())#判斷是否空 ,這裏會返回True,因爲前面放進一個,又取出了一個
print(q.get(timeout=3)) #參數是設置阻塞等待時間。當沒有數據可以取了,就阻塞了

小范例:

import threading,queue

def wri():
    write()

def write():
    for i in range(100):
        q.put(i)
        q.task_done()

def read(name):
    while True:
        if q.empty():
            break
        else:
            print('%s read data:%s'%(name,q.get()))
            q.join()

if __name__=='__main__':

    q = queue.Queue(maxsize=500)
    w1 = threading.Thread(target=wri)
    r1 = threading.Thread(target=read,args=('---01',))
    r2 = threading.Thread(target=read,args=('---------02',))
    r3 = threading.Thread(target=read,args=('----------------03',))

    w1.start()
    r1.start()
    r2.start()
    r3.start()

class queue.LifoQueue(maxsize =0) #後進先出

q = queue.LifoQueue(maxsize=3)#定義長度,後進先出

class queue.PriorityQueue(maxsize=0) #存儲數據室可設置優先級的隊列。

q.get((參數1,數據))參數1是設置優先級。優先級和數據要放在元組裏。數字越小,優先級越高

常用方法

exception queue.Empty

exception queue.Full

Queue.qsize()

Queue.empty() #判斷空

Queue.full() #判斷滿了就返回True

Queue.put(item,block=True,timeout_None) #滿了就放不進

Queue.get_nowait()

Queue.task_done()

多線程的經典——生產者消費者模型

生產者

服務員——queque

消費者

import threading,queue
import time

def consumer(n):#消費者
    while True:#不斷的喫
        print('consumer[%s] get task : %s'% (n,q.get())) #取出對隊列中的數據
        time.sleep(1)#一秒鐘喫一個
        q.task_done()#通知隊列已取出一個數據,隊列-1

def producer(n):#生產者
    count= 1
    while True:#不斷的做
        time.sleep(0.5)#生產一個包子要0.5秒
        if q.qsize()<3: #避免盲目生產,判斷隊列數據小於3才生產
            print('producer[%s] producer a new task: %s' % (n,count))
            q.put(count)#放入隊列
            count +=1
            q.join() #等待數據取完的通知,接不到通知就不進行下一步。隊列爲空時join不阻塞,繼續下一步。
            print('all taks has been cosumed by consumers....')

q = queue.Queue()
#3個消費者
c1 = threading.Thread(target=consumer,args=[1,])
c2 = threading.Thread(target=consumer,args=[2,])
c3 = threading.Thread(target=consumer,args=[3,])
#生產者
p = threading.Thread(target=producer,args=['dralon']) #一個生產者
p2 = threading.Thread(target=producer,args=['xiaozhu']) #又一個生產者
p3 = threading.Thread(target=producer,args=['P3']) #又一個生產者
p4 = threading.Thread(target=producer,args=['P4']) #又一個生產者
p5 = threading.Thread(target=producer,args=['P5']) #又一個生產者


c1.start()
c2.start()
c3.start()
p.start()
p2.start()
p3.start()
p4.start()
p5.start()

Semaphore 信號量

允許多個線程同時運行更改數據
線程間同步和交互

Events 全局標籤 。多個線程可以等待同一個event (類似紅綠燈)

線程間的因果關係體現

(代碼有問題)

import threading
import time
import random

def light():
    if not event.isSet():#判斷是否被設定了,isSet()
        event.set()#wait就不組塞 ,#set()綠燈狀態
    count = 0
    while True:
        if count <10:
            print('--green light on---')
        elif count <13:
            print('--yellow light on---')
        elif count <20:
            if event.isSet():
                event.clear() #清除掉set(),轉變成wait()
            print('--red light on--')
        else:
            count =0
            event.set() #打開set()狀態,就是打開綠燈狀態
def car(n):
    while 1:
        # time.sleep(1)#隨機出現了車
        if event.isSet():#綠燈狀態
            print('car[%s] is running..'%n)
        else:
            print('car [%s] is waiting for the red light...'%n)
            event.wait()

if __name__ == '__main__':
    event = threading.Event()
    light = threading.Thread(target=light)
    light.start()
    for i in range(3):
        t = threading.Thread(target=car,args=(i,))
        t.start()


多進程 multiprocessing庫

基本使用同threading

Process啓動子進程

from multiprocessing import Process
import time

def f(name): #需要運行的方法
    time.sleep(2)
    print('hello',name)

if __name__ == '__main__':
    p1 = Process(target=f,args=('bobo',)) #使用同threading
    p2 = Process(target=f,args=('haha',))
    p1.start()
    p2.start()
    p1.join()

進程間數據傳遞 ——Queue 隊列

先進先出的順序。

實現兩個進程間的數據交換

Queue是多進程安全的隊列,可以使用Queue實現多進程之間的數據傳遞。put方法用以插入數據到隊列中,put方法還有兩個可選參數:blocked和timeout。如果blocked爲True(默認值),並且timeout爲正值,該方法會阻塞timeout指定的時間,直到該隊列有剩餘的空間。如果超時,會拋出Queue.Full異常。如果blocked爲False,但該Queue已滿,會立即拋出Queue.Full異常。

get方法可以從隊列讀取並且刪除一個元素。同樣,get方法有兩個可選參數:blocked和timeout。如果blocked爲True(默認值),並且timeout爲正值,那麼在等待時間內沒有取到任何元素,會拋出Queue.Empty異常。如果blocked爲False,有兩種情況存在,如果Queue有一個值可用,則立即返回該值,否則,如果隊列爲空,則立即拋出Queue.Empty異常。

兩個方法:

put() #放進

get() #取出

from multiprocessing import Process ,Queue

def f(q):
    q.put([42,None,'hello']) #子進程放入數據

if __name__ == '__main__':
    q = Queue() #實例化
    p = Process(target=f,args=(q,)) #創建子進程,調用f方法,把q傳進去
    p.start()
    print(q.get()) #取出數據
    p.join()

在父進程中創建兩個子進程,一個往Queue裏寫數據,一個從Queue裏讀數據:

from multiprocessing import Process,Queue
import os,time,random

def putf(q):
    print('Process to write: %s' % os.getpid())
    for value in ['a','b','c']:
        print('Put %s to queue...'%value)
        q.put(value)
        time.sleep(random.random())

def getf(q):
    print('Process to read %s'% os.getpid())
    while True:
        value = q.get(True)
        print('Get %s from queue'% value)


if __name__ =='__main__':
    q = Queue()
    pw = Process(target=putf,args=(q,))
    pr = Process(target=getf,args=(q,))
    pw.start()
    pr.start()
    pw.join()
    pr.terminate()
輸出結果:

Process to read 11172
Process to write: 12640
Put a to queue...
Get a from queue
Put b to queue...
Get b from queue
Put c to queue...
Get c from queue
一個寫,兩個讀範例:

from multiprocessing import Process,Queue
import os,time,random

def putf(q):
    print('Process to write: %s' % os.getpid())
    for value in ['a','b','c','d','e','f','g','h','i']:
        print('%s Put %s to queue...'%(os.getpid(),value))
        q.put(value)
        time.sleep(random.random())

def getf(q):
    print('Process to read %s'% os.getpid())
    while True:
        value = q.get(True)
        print('%s Get %s from queue'% (os.getpid(),value))



if __name__ =='__main__':
    q = Queue()
    pw = Process(target=putf,args=(q,))#一個寫
    pr = Process(target=getf,args=(q,))#兩個讀
    pr2 = Process(target=getf,args=(q,))

    pw.start()
    pr.start()
    pr2.start()
    pw.join()
    pr.terminate()
    pr2.terminate()
輸出結果:

Process to write: 13184
13184 Put a to queue...
Process to read 12644
12644 Get a from queue
Process to read 3684
13184 Put b to queue...
12644 Get b from queue
13184 Put c to queue...
3684 Get c from queue
13184 Put d to queue...
12644 Get d from queue
13184 Put e to queue...
3684 Get e from queue
13184 Put f to queue...
12644 Get f from queue
13184 Put g to queue...
3684 Get g from queue
13184 Put h to queue...
12644 Get h from queue
13184 Put i to queue...
3684 Get i from queue

小范例:函數間調用函數

from multiprocessing import Process,Queue

def write(q):
    wone(q)

def wone(q):
    wtoo(q)


def wtoo(q):
    try:
        for i in range(100):
            q.put(i)
    except:
        pass

def reader(q,name):
    try:
        while True: #循環收取
            print('%s get:'%name,q.get()) #打印收取的內容,並顯示是誰收取了
            if q.empty(): #判斷隊列是否爲空,真則退出循環
                print('is over')
                break
    except:
        pass

if __name__ == '__main__':
    q = Queue()
    pw = Process(target=write,args=(q,))
    pr = Process(target=reader,args=(q,'---01'))
    pr2 = Process(target=reader,args=(q,'------02------'))
    pr3 = Process(target=reader,args=(q,'+++---03---+++'))

    pw.start()
    pr.start()
    pr2.start()
    pr3.start()
    pw.join()
    pr.terminate()
    pr2.terminate()
    pr3.terminate()

進程間數據傳遞——Pipe 管道

雙向

from multiprocessing import Process ,Pipe

def f(conn):
    conn.send([42,None,'hello']) #子進程放入數據
    conn.close()#子進程關閉管道

if __name__ == '__main__':
    parent_conn,child_conn = Pipe()
    p = Process(target=f,args=(child_conn,)) #創建子進程,調用f方法,把子進程傳進去
    p.start()
    print(parent_conn.recv()) #父進程取出數據
    p.join()
數據共享方法——Manager

進程數據共享

from multiprocessing import Process,Manager

def f(d,l,n): #d代表dict,l代表list。子線程往同一個list裏寫數據。
    d[1]='1'
    d['2'] = 2
    d[0.25] =None
    l.append(n)
    print(l)

if __name__ == '__main__':
    with Manager() as manager:
        d = manager.dict() #通過manager生成dict
        l = manager.list(range(5))#通過manager生成list
        p_list = []
        for i in range(10): #創建了10個進程
            p = Process(target=f,args=(d,l,i))
            p.start()
            p_list.append(p)
        for res in p_list:
            res.join()
        print(d)
        print(l)

這個範例更好:

from multiprocessing import Process,Manager
import random

def f(l,i):
    l.append(i)#哪個進程調用這個方法,列表就將此進程傳入的隨機數添加進列表。實現多個進程操作一個列表數據的寫入
    print(l)


if __name__=='__main__':

    manager = Manager()
    list = manager.list()#建立一個空列表
    p_list = []
    for i in range(10):
        j = random.random()
        p = Process(target=f,args=(list,j))#每循環一次,進程往list寫如一個隨機數。
        p.start()
        p_list.append(p)
    for res in p_list:
        res.join()

進程同步——Lock

當多個進程需要訪問共享資源的時候,Lock可以用來避免訪問的衝突。

from multiprocessing import Process,Lock

def f(l,i):
    l.acquire() #啓動鎖
    try:
        print('hello world',i)
    finally:
        l.release() #釋放鎖

if __name__ =='__main__':
    lock = Lock() #實例化鎖

    for num in range(10):
        Process(target=f,args=(lock,num)).start() #創建10個進程並啓動,並將鎖傳進去
進程池

進程池內部維護一個進程序列。允許同一時刻最多有多少個進程運行

兩個方法

apply (同步,同步就變成串行,同步的時候不能使用回調)

apply_async (異步)

from multiprocessing import Process,Pool,freeze_support #windows中要導入freeze_support
import time

def Foo(i): #進程調用的方法
    time.sleep(2)
    return i+100

def Bar(arg):
    print('--->exec done:',arg)

if __name__ == '__main__': #windows中要加入這兩句纔不會出錯
    freeze_support()

    pool = Pool(5) #允許最大5個進程同時運行

    for i in range(10):
        pool.apply_async(func=Foo,args=(i,),callback=Bar) #進程池格式.callback回調,FOO執行的結果返回給Bar

    print('end')
    pool.close()
    pool.join() #進程池中進程執行完畢後再關閉。如果註釋,那麼程序就不等待子進程了














發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章