1. 程式人生 > >getName()--ident--enumerate--activeCount--執行緒池--協程(gevent)--優先順序佇列

getName()--ident--enumerate--activeCount--執行緒池--協程(gevent)--優先順序佇列

一、如何檢視執行緒的id和名字

方法介紹:
    threading.current_thread().getName()  #檢視執行緒的名字
    threading.current_thread().ident  #檢視執行緒的id
    threading.enumerate(): 返回一個包含正在執行的執行緒的list。正在執行指執行緒啟動後、結束前,不包括啟動前和終止後的執行緒。
    threading.activeCount(): 返回正在執行的執行緒數量,與len(threading.enumerate())有相同的結果。
import threading
import time from threading import Thread,current_thread def f1(n): pass # time.sleep(1) # print("子執行緒名字",current_thread().getName()) # print("%s號執行緒的任務"%n) if __name__ == '__main__': t1 = Thread(target=f1,args=(1,)) t1.start() print("主執行緒名字",current_thread().getName()) #檢視執行緒的名字
print("主執行緒id",current_thread().ident())  #檢視執行緒的id print(current_thread()) #列印結果為執行緒的名字 print(threading.enumerate()) #返回包含正在執行的執行緒的list print(threading.activeCount()) #返回正在執行的執行緒的數量

二、執行緒池

執行緒池的作用:
    服務開啟的程序數或者執行緒數都會隨著併發的額客戶端數目的增多而增多,這會對伺服器主機帶來巨大的壓力,甚至於不堪重負而癱瘓,所以我們必須對服務開啟的程序數或執行緒數加以控制.
執行緒池個數預設是
cpu個數*5,開啟執行緒池和程序池,就一個模組的名字不一樣,其他方法都一樣,之前學的pool方法開啟程序池是一種補充.
concurrent.futures  #需要匯入的模組 (模組提供了高度封裝的非同步呼叫介面)
ThreadPoolExecutor #執行緒池(提供非同步呼叫)

ProcessPoolExecutor #程序池(提供非同步呼叫)
基本方法:
submit() #給執行緒池非同步提交任務
   p = ThreadPoolExecutor(4) #預設的執行緒個數是cpu個數*5
   p.map(f1,可迭代的物件) #非同步執行,map 取代for 迴圈submit的操作

   shutdown()#鎖定執行緒池,等待執行緒池中所有以提交任務全部執行完畢. 相當於程序池的pool.close()+pool.join()
   add_done_callback(fn) #回撥函式
   res.result() #取得結果,獲取返回值,和get方法一樣,如果沒有返回值,會等待,阻塞程式
注意:

  submit和map必須在shutdown之前
#開啟執行緒池
from
concurrent.futures import ThreadPoolExecutor def f1(n): return n*n if __name__ == '__main__': tp = ThreadPoolExecutor(4)
   tp.map(f1,range(10)) #非同步提交任務,引數同樣是任務名稱,可迭代物件 res_list
= [] for i in range(10): res = tp.submit(f1,i) #submit是給執行緒池非同步提交任務 res_list.append(res) tp.shutdown() #主執行緒等待所有提交給執行緒池的任務,全部執行完畢(相當於close+join) for r in res_list: print(r.result()) print("主執行緒結束")
#建立程序
from
concurrent.futures import ProcessPoolExecutor def f1(n): return n*n if __name__ == '__main__': tp = ProcessPoolExecutor(4) # tp.map(f1,range(10)) #非同步提交任務,引數同樣是任務名稱,可迭代物件 res_list = [] for i in range(10): res = tp.submit(f1,i) #submit是給執行緒池非同步提交任務 res_list.append(res) tp.shutdown() #主執行緒等待所有提交給執行緒池的任務,全部執行完畢(相當於close+join) for r in res_list: print(r.result()) print("主程序結束")

 執行緒池的回撥函式

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
def f1(n,s):
    return n+s
def f2(n):
    print("回撥函式>>",n.result()) #此處加result是取裡面值,否則出來的是一個結果集地址
if __name__ == '__main__':
    tp = ThreadPoolExecutor(4)
    res = tp.submit(f1,11,12).add_done_callback(f2)

 三、協程 gevent

什麼是協程:
    單執行緒下的併發稱為協程\微執行緒\纖程.協程是一種使用者態的輕量級執行緒,即協程是由使用者程式自己控制排程
注意:
    1.python 的執行緒屬於核心級別的,即由作業系統的控制呼叫(如單執行緒遇到IO或者執行時間過長
就會被迫交出cpu執行許可權,切換其他執行緒執行)
    2.單執行緒內開啟協程,一旦遇到IO,就會從應用程式級別(而非作業系統)控制切換,一次來提升效率)(非IO操作的切換與效率無關)
缺點:
    1.協程的本質是單執行緒下,無法利用多核,可以是一個程式開啟多個程序,每個程序內開啟多個執行緒,每個執行緒內開啟協程
    2.協程指的是單個執行緒,因而一旦協程出現阻塞,將會阻塞整個執行緒
什麼是gevent:
    gevent是一個第三方庫,可以實現併發同步或非同步程式設計,一個協程遇到IO操作自動切換到其他協程.
引數介紹:
g1 = gevent.spawn(func2,1)#建立一個協程物件g1,引數1是函式名,後面的引數都是傳遞給該函式的形參.
gevent.joinall([g1,g2])  #相當於g1.join()和g2.join()
g1.value #拿到func1的返回值

 

如何確定是一個協程,驗證它在一個執行緒裡面,而沒有開啟其他執行緒:
print(threading.current_thread().getName())  #放線上程裡面,返回的值是DummyThread-n,即假執行緒,是一個協程

 

具體操作:
遇到IO會自動切換任務
import gevent
from gevent import monkey;monkey.patch_all() #可以理解為給gevent打補丁,讓它能夠知道time.sleep(2)跟gevent.sleep(2)一樣,該補丁必須放在打補丁的前面,如time,socket模組之前.def eat(name):
    print("%seat 1"%name)
    time.sleep(2)
    print("%s eat 2"%name)
def play(name):
    print("%s play 1"%name)
    time.sleep(1)
    print("%s play 2"%name)
g1 = gevent.spawn(eat,"egon")
g2 = gevent.spawn(play,name="egon")
gevent.joinall([g1,g2]) #gevent.joinall([g1,g2]) = g1.join()和g2.join()
print("")
列印結果:
egoneat 1
egon play 1
egon play 2
egon eat 2

四、greenlet模組

如果我們在單個執行緒你有20個任務,想要實現在多個任務之間切換,使用yield生成器的方式過於麻煩(需要初始化一次生成器,然後再呼叫send...非常麻煩),而使用greenlet模組可以非常簡單的實現這20個任務直接的切換.

但是在沒有IO的情況下或者沒有重複開闢的記憶體空間的操作,反而會降低程式的執行速度.

greenlet只是提供了一種比generator更加便捷的切換方式.當切到一個任務執行時遇到IO,就原地阻塞,仍然沒有解決遇到IO自動切換來提升效率的問題

 

五、先進後出和優先順序佇列

先進後出佇列:
  queue.LifoQueue() #先進後出佇列   注意:先進去的,最後出來
優先順序佇列:
  queue,priorityQueue() #儲存資料時可設定優先順序的佇列
  注意:put 的資料是一個元組,元組的第一個引數是優先順序數字,數字越小,優先順序越高,越先被get到取出來,第二個引數是put進去的的值1
   如果優先順序相同,將會比較值,值不能是字典資料型別.
#先進後出例項
import queue
q=queue.LifoQueue() #佇列,類似於棧,棧我們提過嗎,是不是先進後出的順序啊
q.put('first')
q.put('second')
# q.put_nowait()

print(q.get())
print(q.get())
# q.get_nowait()
'''
結果(後進先出):
third
second
first
'''
 
#優先順序佇列演示
import queue q = queue.PriorityQueue(5) #注意佇列的長度,因為後面put多了,會阻塞 q.put((9,"小黑")) q.put((6,"大黑")) q.put((7,"不黑")) #如果優先順序數字相同,會比較第二個引數的元素的ascii表中的位置,如果資料型別不同會報錯.
q.put((7,"很黑")) print(q.get()) print(q.get()) print(q.get())