1. 程式人生 > >Python多線程總結

Python多線程總結

變量 get 等待事件 連接 情況 __init__ 守護進程 end 線程間交換數據

threading 線程是操作系統能夠進行運算調度的最小單位。若幹個線程組成一個進程,一個進程至少有一個線程。

Python的標準庫提供了兩個模塊:_thread和threading,後者是對前者的高級封閉。絕大多數情況下我們只需要使用threading這個高級模塊。

threading模塊提供的類:

  Thread,Event

threading模塊的常用方法和屬性:

  threading.current_thread().name:返回當前線程的線程名。

  threading.activeCount():返回活動的線程數量。

Thread類

  有兩種方法來使用thread類創建線程。

import threading,time
#方法一:將要執行的方法作為參數傳給Thread的構造方法
def run(arg):
    print(thread %s is running...%arg)
    time.sleep(1)
for i in range(5):
    t = threading.Thread(target=run,args=(i,))
    t.start()

print(thread %s is end! %threading.current_thread().name)
import threading,time
#方法二:從Thread繼承,並重寫run()
class MyThread(threading.Thread): def __init__(self,arg): super(MyThread,self).__init__() self.arg = arg def run(self):#重寫run(),定義要運行的函數 print(thread %s is running...%self.arg) time.sleep(1) for i in range(5): t = MyThread(i) t.start() print(thread %s is end!
%threading.current_thread().name)

構造方法:

Thread(self, group=None, target=None, name=None,args=(), kwargs=None, *, daemon=None):

  target:要執行的方法,比如方法一中的run。此處不用加(),因為不執行方法只傳方法名;

  args/kwargs:方法的參數;

  name:線程名 就是 threading.current_thread().name 的值

實例方法:

  start():啟動線程

  join(timeout=None):阻塞當前上下文環境的線程,直到調用此方法的線程終止或到達指定的timeout(可選參數);

  setDaemon(bool):設置為守護進程。註意:需要在start()之前設置。

    設置為守護進程:主線程結束,守護進程自動結束,無論當時守護進程為何執行狀態。

    不為守護進程:主線程等待此線程結束後方可結束。

  守護進程例子:

  例一:默認情況下不設置為守護進程  

import threading,time

def run(arg):
    time.sleep(2)
    print(thread %s is running...%s%(arg,time.ctime()))

for i in range(5):
    t = threading.Thread(target=run,args=(i,))
    # t.setDaemon(True)
    t.start()

print(thread %s is end! %threading.current_thread().name)
#可看到系統同時啟動了5個子線程,等待全部執行結束後,才結束主線程。

  例一:設置為守護進程

import threading,time

def run(arg):
    time.sleep(2)
    print(thread %s is running...%s%(arg,time.ctime()))

for i in range(5):
    t = threading.Thread(target=run,args=(i,))
    t.setDaemon(True)
    t.start()

print(thread %s is end! %threading.current_thread().name)
#可看到只打印出‘thread MainThread is end!’,子線程未執行完成,主結束已完成結束。

  join()使用

import threading,time

def run(arg):
    time.sleep(3)
    print(thread %s is running...%s%(arg,time.ctime()))

thread_list = []
for i in range(5):
    t = threading.Thread(target=run,args=(i,))
    t.setDaemon(True)
    t.start()
    thread_list.append(t) #為了不阻塞後面的線程的啟動,不在這裏join,先放到一個列表裏
for res in thread_list: #循環線程實例列表,等待所有線程執行完畢
    res.join()

print(thread %s is end! %threading.current_thread().name)

線程間交換數據

方法一:通過一個全局變量

import threading
def run():
    global sum  #申明使用全局變量sum(並非生成一個全局變量sum)
    sum += 1
sum = 0
thread_list = []
for i in range(50):
    t = threading.Thread(target=run,args=())
    t.start()
for res in thread_list:
    res.join()
print(all threads has done...,threading.current_thread().name,threading.active_count())
print(sum:,sum)

  以上代碼塊中,對全局變量並沒有加上線程鎖。在Python3中,一般不會出什麽問題,但官方也並沒有明確表示會默認在後臺加上鎖,所以還是推薦手動加上鎖。但在Python2中,極有可能會出現計算的值比理想中的值小的情況。為什麽會出現這種情況呢,詳情請見下圖:(圖來自金角大王)

技術分享圖片

  一個加鎖後的代碼:  

import threading
lock = threading.Lock() #創建一個鎖實例
def run():
    lock.acquire()  #加鎖
    global sum
    sum += 1
    lock.release()  #解鎖
sum = 0
thread_list = []
for i in range(50):
    t = threading.Thread(target=run,args=())
    t.start()
for res in thread_list:
    res.join()
print(all threads has done...,threading.current_thread().name,threading.active_count())
print(sum:,sum)

  值得註意的是,在很少的情況下,鎖中套鎖,多道鎖的情況下一定要使用遞歸鎖,可避免程序被自己鎖死。方法很簡單:

lock = threading.RLock()   #生成一個遞歸鎖實例,即可

方法二:隊列

  隊列是一個容器,類似於列表。但與列表有明顯的區別!從列表中讀取數據後,列表中的數據不會消失。但從隊列中讀取數據,此數據會從原隊列中被取走,不再存在於隊列中。  

  作用:

    1.   解耦,使程序之間實現松耦合(一個模塊修改了不影響另一個調用此模塊的模塊)
    2.   提高處理效率

  隊列類型:

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

    後入先出  class queue.LifoQueue(maxsize=0)   #last in first out

    自定義順序 class queue.PriorityQueue()

  三種隊列舉例:  

import queue
#先入先出隊列
q = queue.Queue()  #默認不限制隊列大小,也可指定maxsize=
q.put(d1) #向隊列中放入數據
q.put(d2)
q.put(d3)
b = q.qsize()   #大小要在這裏先得到,如果放在while中,q.qsize會不斷變小,取出一個小一個
i = 0
while i < b:
    print(q.get())
    i += 1

#創建一個後進先出隊列
q = queue.LifoQueue()
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())

#創建一個優先級隊列
q = queue.PriorityQueue()
q.put((10,lyj))
q.put((-1,mxi))
q.put((3,ww))
q.put((11,aa))
print(q.get())
print(q.get())
print(q.get())
print(q.get())

信號量

  與鎖功能相同,只不過信號量規定了多把鎖的存在,規定了最多同時執行的線程數。

  作用:像數據庫這類應用,規定只有100個客戶端可同時連接執行操作,超過此量會引起性能衰減。就可以使用信號量來規定最大放行數量。

import threading,time
def run(n):
    semaphore.acquire() #信號量加鎖
    time.sleep(1)
    print(run the thread:%s %n)
    semaphore.release() #信號量解鎖

if __name__ == __main__:
    semaphore = threading.BoundedSemaphore(5) #最多允許5個線程同時運行
    for i in range(22):
        t = threading.Thread(target=run,args=(i,))
        t.start()
while threading.active_count() != 1:
    pass
else:
    print(----all threads done---)
#註意:程序執行效果可能為5個同時結束,同時開始,實際上並非同時開始,只是總量為5個,先進先出執行。

Event類

  Event(事件)是最簡單的線程通信機制之一:一個線程通知事件,其他線程等待事件。Event內置了一個初始為False的標誌,當調用set()時設為True,調用clear()時重置為 False。wait()將阻塞線程至等待阻塞狀態。

  構造方法:

  Event()

  實例方法:

  set():將標誌位設置為True,阻塞線程轉為放行狀態(綠燈

  clear():將標誌位設置為False,放行狀態轉為阻塞狀態(紅燈

  is_set():檢測標誌位是否為True,返回True,否則返回False

  wait([timeout]):如果標誌為True將立即返回,否則阻塞線程至等待阻塞狀態,等待其他線程調用set()。

代碼實例:紅綠燈

import time,threading
event = threading.Event()
event.set()
def lighter():
    count = 0
    while True:
        if count > 20 and count < 30: #改成紅燈
            event.clear() #把標誌位清除
            print(\033[41;1mred light is on...\033[0m)
        elif count > 30:
            event.set() #變綠燈
            count = 0
        else:
            print(\033[42;1mgreen light is on...\033[0m)
        time.sleep(1)
        count += 1
def car(name):
    while True:
        if event.is_set():  #代表綠燈
            print([%s] running... % name)
            time.sleep(1)
        else:
            print(waiting...)
            event.wait() #阻塞線程

lighter = threading.Thread(target=lighter,) lighter.start() car1 = threading.Thread(target=car,args=(TOYOTO,)) car1.start()

生產者消費者模型

  生產者負責向容器(如隊列)內投放數據,消費者從容器中取數據使用。達到生產者與消費者彼此之間不直接通訊的目的。容器就相當於一個緩沖區,平衡了生產者和消費者的處理能力。

  生產者消費者模型實現了程序的解耦(一個模塊進行了修改,不影響另一個調用它的模塊)

舉例:

import threading,time,queue

q = queue.Queue(maxsize=10)
def producer(name):
    count = 0
    while True:
        q.put(骨頭%s%count)
        print(%s生產了第%s塊骨頭 %(name,count))
        count += 1
        time.sleep(1)
def consumer(name):
    while True:
        print(%s吃了%s%(name,q.get()))
        time.sleep(1)

p = threading.Thread(target=producer,args=(lyj,))
c1 = threading.Thread(target=consumer,args=(dog,))
c2 = threading.Thread(target=consumer,args=(meixi,))
p.start()
c1.start()
c2.start()

參考文章:https://www.cnblogs.com/tkqasn/p/5700281.html

Python多線程總結