1. 程式人生 > >33 python Threading模組與執行緒 執行緒池 GIL鎖

33 python Threading模組與執行緒 執行緒池 GIL鎖

---恢復內容開始---

一 python Threading模組與執行緒

  multiprocess模組完全模仿了threading模組的介面,二者在使用層面有較大的相似處。

1. 執行緒的建立

from threading import Thread
def func(m):
    print(m)

if __name__ == '__main__':
    f = Thread(target=func,args=(1,)) #建立一個執行緒物件
    f.start()
    print('主程序結束')
方式一
from
threading import Thread class Mythread(Thread): def __init__(self,m): super().__init__()#繼承Thread模組的init self.m = m def run(self): print('拉拉愛了') def norun(self): #還可以寫其他的方法 print('啦啊的') if __name__ == '__main__': m = Mythread('e') #傳遞自己的變數 m.start() m.norun()
方式二

2.多執行緒和多程序執行的效率對比

ort time
from threading import Thread
from multiprocessing import Process

def func():
    pass

if __name__ == '__main__':

    t_list = []
    t_s_t = time.time()
    for i in range(100):
        t = Thread(target=func,)
        t_list.append(t)
        t.start()
    [tt.join() 
for tt in t_list]#等待子程式 t_e_t = time.time() t_dif_t = t_e_t - t_s_t p_list = [] p_s_t = time.time() for i in range(100): p = Process(target=func,) p_list.append(p) p.start() [pp.join() for pp in p_list]#等待子程式 p_e_t = time.time() p_dif_t = p_e_t - p_s_t print('多執行緒的時間>>>',t_dif_t)#0.025949478149414062 print('多程序的時間>>>',p_dif_t)#7.810645341873169 print('主執行緒結束')
效率對比

3 多執行緒資料共享

import time
from threading import Thread

num = 100
def func():
    # time.sleep(3)
    global num
    tep = num
    time.sleep(0.001)
    tep = tep - 1
    num = tep
    # num -= 1

if __name__ == '__main__':
    t_list = []
    for i in range(100):
        t = Thread(target=func,)
        t_list.append(t)
        t.start()
    [tt.join() for tt in t_list]
 
    print('主執行緒的num',num) #87 由於共享時會爭搶資料並沒有變為0
驗證
import time
from threading import Thread,Lock

num = 100
def func(tl):
    time.sleep(1)
    global num
    tl.acquire()
    tep = num
    time.sleep(0.001)
    tep = tep - 1
    num = tep
    tl.release()

if __name__ == '__main__':
    tl = Lock()
    t_list = []
    for i in range(10):
        t = Thread(target=func,args=(tl,))
        t_list.append(t)
        t.start()
    [tt.join() for tt in t_list]
    # t.join()
    print('主執行緒的num',num)
枷鎖解決資料共享不安全

4 守護執行緒守護程序

import time
from threading import Thread
from multiprocessing import Process

def func1():
    time.sleep(3)
    print('任務1結束')

def func2():
    time.sleep(2)
    print('任務2結束')

if __name__ == '__main__':
    p1 = Process(target=func1,)
    p2 = Process(target=func2,)
    #守護程序:主程序程式碼結束,守護程序結束,由於程式具有回收機制,所以要等待非守護子程序執行完,才結束。
    p1.daemon = True
    # p2.daemon = True
    p1.start()
    p2.start()
    t1 = Thread(target=func1,)
    t2 = Thread(target=func2,)
    t1.daemon = True
    t2.setDaemon(True)  #將t2設定為守護執行緒
    #守護執行緒:主程式等所有非守護執行緒結束才結束
    t1.setDaemon(True)  #將t1設定為守護執行緒
    t1.start()
    t2.start()
    print('主程序結束')
守護執行緒

5 訊號量

import time
import random
from threading import Thread,Semaphore

def func1(i,s):
    s.acquire()
    # time.sleep(1)
    print('客官%s裡邊請~~'%i)
    time.sleep(random.randint(1, 3))
    s.release()


if __name__ == '__main__':
    s = Semaphore(4)
    for i in range(10):
        t = Thread(target=func1,args=(i,s))
        t.start()
訊號量

6 事件

事件的
from threading import Thread,Event

e = Event()  #預設是False,

# print(e.isSet())
print('開始等啦')

e.set() #將事件物件的狀態改為True
print(e.isSet())
# e.clear() #將e的狀態改為False

e.wait()  #如果e的狀態為False,就在這裡阻塞
print('大哥,還沒完事兒,你行')

7 執行緒佇列

import queue      #先進先出佇列
q = queue.Queue(3)
q.put('first')
q.put('second')
q.put(3)
# q.put(6)  #佇列長度為3 當放過超過這個長度時程式就會一直等待前面的元素被拿
# 此時就可以用put_nowait()配合try來使用
try:
    q.put_nowait(3)
except:
    print('佇列滿了')
# print(q.qsize())#佇列長度

print(q.get())
print(q.get())
print(q.get())
try:
    print(q.get_nowait())
except:
    print('佇列沒有了')


import queue      #先進後出佇列
q = queue.LifoQueue()
q.put(1)
q.put(2)
q.put(3)
print(q.get()) #3
print(q.get())#2
print(q.get())#1

import queue
q = queue.PriorityQueue()#自定義排序
q.put((0,10))
q.put((-1,4))
q.put((6,9))
print(q.get())#(-1, 4)
print(q.get())#(0, 10)
print(q.get())#(6, 9)
幾種佇列

8 執行緒池

import time
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
#上面匯入的是執行緒池和程序池(另外一種匯入法)
from multiprocessing import Pool
def func(n):
    time.sleep(1)
    # print(n*n)
    return n*n
if __name__ == '__main__':
    t = ThreadPoolExecutor(4)
    # p = ProcessPoolExecutor(4) #建立程序池,其他的方法完全一樣
    res_lis = []
    for i in range(10):
        res = t.submit(func,i) #非同步提交執行緒,拿到結果物件
        # print(res.result()) #等待結果阻塞,拿到就再執行下一個迴圈,序列
        res_lis.append(res)
    t.shutdown()  # 放在此處此時所有結果物件都有結果直接全部拿到
    for res in res_lis:
        print(res.result())
    # t.shutdown()  # 等待子執行緒結束,和程序裡面close()+join()差不多,此時上面for結果會四個四個列印結果
    print('主程序結束')

# 程序池
# if __name__ == '__main__':
#     p = Pool(4)
#     for i in range(10):
#         res = p.apply_async(func,(i,))
#         print(res.get())
執行緒池和程序池建立方法

9 執行緒池的map方法

import time
from concurrent.futures import ThreadPoolExecutor

def func(n):
    # print(n)
    time.sleep(1)

    return n*n
if __name__ == '__main__':
    t = ThreadPoolExecutor(4)
    res = t.map(func,range(10))#只是提交任務主程式不會等待
    for m in res:        #生成器物件
        print(m)
    print('主程序結束')
執行緒池map返回值為一個生成器

10 執行緒池,程序池的返回函式

import time
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
from multiprocessing import Pool
def func(n):
    return n*n

def call_back(i):

    print('>>>>',i)
    print(i)

if __name__ == '__main__':
    # p = Pool(4)
    t = ThreadPoolExecutor(4)
    p = ProcessPoolExecutor(4)
    lis = []
    for i in range(10):   #執行緒池的方法
        res = t.submit(func,i).add_done_callback(call_back)
        lis.append(res)   #返回的都是結果物件
    for i in lis:
        print(i.result())

    for i in range(10):  # 程序池的方法
        res = t.submit(func,i).add_done_callback(call_back)

    for i in range(10):
        res = p.apply_async(func,(i,),callback=call_back)
        print(res.get())

    # p.close()
    # p.join()
    print('sssss')
call_back

 二  GIL鎖

Gil鎖(Global Interpreter Lock)

python全域性直譯器鎖,有了這個鎖的存在,python直譯器在同一時間內只能讓一個程序中的一個執行緒去執行,這樣python的多執行緒就無法利用多核優勢,但是這並不是python語言本身的缺點,是直譯器的缺點,這個問題只存在於Cpython解釋其中,像Jpython就沒有。但是Cpthon是python官方直譯器(算目前執行效率最高的吧),所以多數人都以為Gil鎖是python語言的弊端。

 

過程解釋:

1、載入python直譯器程式碼

2、載入自己的py檔案

3、py檔案作為引數傳給直譯器(因為有GIL鎖,一次只能一個執行緒進入)

4、直譯器將py檔案編譯成.pyc位元組碼檔案

5、直譯器通過虛擬機器將位元組碼檔案轉為二進位制檔案

6、二進位制檔案等待cpu呼叫

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

---恢復內容結束---