day9---多線程,線程鎖,隊列

進程、線程

http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

使用threading模塊實現多線程編程[綜述]
Python這門解釋性語言也有專門的線程模型,Python虛擬機使用GIL(Global Interpreter Lock,全局解釋器鎖)來互斥線程對共享資源的訪問,但暫時無法利用多處理器的優勢。

        在Python中我們主要是通過thread和 threading這兩個模塊來實現的,其中Python的threading模塊是對thread做了一些包裝的,可以更加方便的被使用,所以我們使用 threading模塊實現多線程編程。這篇文章我們主要來看看Python對多線程編程的支持。

        在語言層面,Python對多線程提供了很好的支持,可以方便地支持創建線程、互斥鎖、信號量、同步等特性。下面就是官網上介紹threading模塊的基本資料及功能:

實現模塊
        thread:多線程的底層支持模塊,一般不建議使用;

        threading:對thread進行了封裝,將一些線程的操作對象化。

threading模塊
        Thread 線程類,這是我們用的最多的一個類,你可以指定線程函數執行或者繼承自它都可以實現子線程功能;

        Timer與Thread類似,但要等待一段時間後纔開始運行;

        Lock 鎖原語,這個我們可以對全局變量互斥時使用;

        RLock 可重入鎖,使單線程可以再次獲得已經獲得的鎖;

        Condition 條件變量,能讓一個線程停下來,等待其他線程滿足某個“條件”;

        Event 通用的條件變量。多個線程可以等待某個事件發生,在事件發生後,所有的線程都被激活;

        Semaphore爲等待鎖的線程提供一個類似“等候室”的結構;

        BoundedSemaphore 與semaphore類似,但不允許超過初始值;

        Queue:實現了多生產者(Producer)、多消費者(Consumer)的隊列,支持鎖原語,能夠在多個線程之間提供很好的同步支持。

其中Thread類
        是你主要的線程類,可以創建進程實例。該類提供的函數包括:

        getName(self) 返回線程的名字

        isAlive(self) 布爾標誌,表示這個線程是否還在運行中

        isDaemon(self) 返回線程的daemon標誌

        join(self, timeout=None) 程序掛起,直到線程結束,如果給出timeout,則最多阻塞timeout秒

        run(self) 定義線程的功能函數

        setDaemon(self, daemonic) 把線程的daemon標誌設爲daemonic

        setName(self, name) 設置線程的名字

        start(self) 開始線程執行

其中Queue提供的類
        Queue隊列

        LifoQueue後入先出(LIFO)隊列

        PriorityQueue 優先隊列

Python threading模塊

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author:DCC
import threading
import time
def run(n):
    print("task",n)
    time.sleep(2)
t1 = threading.Thread(target=run,args=("t1",))
t2 = threading.Thread(target=run,args=("t2",))
t1.start()
t2.start()
print(t1.getName())
print(t2.getName())
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author:DCC
import threading
import time
class MyThread(threading.Thread):
    def __init__(self,n):
        super(MyThread,self).__init__() #注意:一定要顯式的調用父類的初始化函數。
        self.n = n
    def run(self):     #重寫父類run方法,在線程啓動後執行該方法內的代碼。
        print("running task",self.n)
        time.sleep(3)   
t1 = MyThread("t1")
t2 = MyThread("t2")
if __name__ == '__main__':
    t1.start()
    t2.start()
    print(t1.getName())
    print(t2.getName())


Join & Daemon

一般情況下 主線程是不等待子線程是否執行完成的,只是觸發一下,就不管了。


1、join ()方法:主線程A中,創建了子線程B,並且在主線程A中調用了B.join(),那麼,主線程A會在調用的地方等待,直到子線程B完成操作後,纔可以接着往下執行,那麼在調用這個線程時可以使用被調用線程的join方法。
原型:join([timeout])
裏面的參數時可選的,代表線程運行的最大時間,即如果超過這個時間,不管這個此線程有沒有執行完畢都會被回收,然後主線程或函數都會接着執行的。

import threading  
import time  
class MyThread(threading.Thread):  
        def __init__(self,id):  
                threading.Thread.__init__(self)  
                self.id = id  
        def run(self):  
                x = 0  
                time.sleep(10)  
                print self.id  
  
if __name__ == "__main__":  
        t1=MyThread(999)  
        t1.start()  
        for i in range(5):  
                print i  
#執行結果
0  
1  
2  
3  
4  
999

機器上運行時,4和999之間,有明顯的停頓。解釋:線程t1 start後,主線程並沒有等線程t1運行結束後再執行,而是先把5次循環打印執行完畢(打印到4),然後sleep(10)後,線程t1把傳進去的999纔打印出來。
現在,我們把join()方法加進去(其他代碼不變),看看有什麼不一樣,例子:

import threading  
import time  
class MyThread(threading.Thread):  
        def __init__(self,id):  
                threading.Thread.__init__(self)  
                self.id = id  
        def run(self):  
                x = 0  
                time.sleep(10)  
                print self.id  
   
if __name__ == "__main__":  
        t1=MyThread(999)  
        t1.start()  
        t1.join()  
        for i in range(5):  
                print i 
#執行結果
999  
0  
1  
2  
3  
4

2、setDaemon()方法。主線程A中,創建了子線程B,並且在主線程A中調用了B.setDaemon(),這個的意思是,把主線程A設置爲守護線程,這時候,要是主線程A執行結束了,就不管子線程B是否完成,一併和主線程A退出.這就是setDaemon方法的含義,這基本和join是相反的。此外,還有個要特別注意的:必須在start() 方法調用之前設置,如果不設置爲守護線程,程序會被無限掛起。
例子:就是設置子線程隨主線程的結束而結束:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author:DCC
import threading
import time
def run(n):
    print("task",n)
    time.sleep(2)
    print("thread done...",n)
start_time = time.time()
t_objs = [] #存線程實例
for i in range(50):
    t = threading.Thread(target=run,args=("t-%s"% i,))
    t.setDaemon(True) #把當前線程設置爲守護線程
    t.start()
    t_objs.append(t) # 爲了不阻塞後面線程的啓動,不在這裏join,先放到-個列表裏
    # print(t.getName())
#time.sleep(2)
print(threading.active_count())
# for i in t_objs: #循環線程實例列表,等待所有線程執行完畢
#     t.join()
print(time.time()-start_time)


 線程鎖

CPU執行任務時,在線程之間是進行隨機調度的,並且每個線程可能只執行n條代碼後就轉而執行另外一條線程。由於在一個進程中的多個線程之間是共享資源和數據的,這就容易造成資源搶奪或髒數據,於是就有了鎖的概念,限制某一時刻只有一個線程能訪問某個指定的數據。

未枷鎖

import threading
import time
NUM = 0
def show():
    global NUM
    NUM += 1
    name = t.getName()
    time.sleep(1)       # 注意,這行語句的位置很重要,必須在NUM被修改後,否則觀察不到髒數據的現象。
    print(name, "執行完畢後,NUM的值爲: ", NUM)
 
for i in range(10):
    t = threading.Thread(target=show)
    t.start()
print('main thread stop')

LOCK鎖

普通鎖,也叫互斥鎖,是獨佔的,同一時刻只有一個線程被放行。

import time
import threading
NUM = 10
def func(lock):
    global NUM
    lock.acquire()  # 讓鎖開始起作用
    NUM -= 1
    time.sleep(1)
    print(NUM)
    lock.release()  # 釋放鎖
lock = threading.Lock()  # 實例化一個鎖對象
for i in range(10):
    t = threading.Thread(target=func, args=(lock,))  # 記得把鎖當作參數傳遞給func參數
    t.start()


RLock(遞歸鎖)

說白了就是在一個大鎖中還要再包含子鎖

threading模塊的Lock類,它不支持嵌套鎖。RLcok類的用法和Lock一模一樣,但它支持嵌套,因此我們一般直接使用RLcok類。

import threading, time

def run1():
    print("grab the first part data")
    lock.acquire()
    global num
    num += 1
    lock.release()
    return num
def run2():
    print("grab the second part data")
    lock.acquire()
    global num2
    num2 += 1
    lock.release()
    return num2
def run3():
    lock.acquire()
    res = run1()
    print('--------between run1 and run2-----')
    res2 = run2()
    lock.release()
    print(res, res2)
if __name__ == '__main__':
    num, num2 = 0, 0
    lock = threading.RLock()
    for i in range(10):
        t = threading.Thread(target=run3)
        t.start()
while threading.active_count() != 1:
    print(threading.active_count())
else:
    print('----all threads done---')
    print(num, num2)


時器(Timer)

定時器,指定n秒後執行某操作。很簡單但很使用的東西。

from threading import Timer

def hello():

    print("hello, world")

t = Timer(1, hello)  # 表示1秒後執行hello函數

t.start()


信號量(Semaphore)

這種鎖允許一定數量的線程同時更改數據,它不是互斥鎖。比如地鐵安檢,排隊人很多,工作人員只允許一定數量的人進入安檢區,其它的人繼續排隊。

互斥鎖 同時只允許一個線程更改數據,而Semaphore是同時允許一定數量的線程更改數據 ,比如廁所有3個坑,那最多隻允許3個人上廁所,後面的人只能等裏面有人出來了才能再進去。

import time
import threading
 
def run(n):
    semaphore.acquire()
    print("run the thread: %s" % n)
    time.sleep(1)
    semaphore.release()
 
num = 0
semaphore = threading.BoundedSemaphore(5)  # 最多允許5個線程同時運行
for i in range(20):
    t = threading.Thread(target=run, args=(i,))
    t.start()


事件(Event)

事件主要提供了三個方法 set、wait、clear。

事件機制:全局定義了一個“Flag”,如果“Flag”的值爲False,那麼當程序執行wait方法時就會阻塞,如果“Flag”值爲True,那麼wait方法時便不再阻塞。這種鎖,類似交通紅綠燈(默認是紅燈),它屬於在紅燈的時候一次性阻擋所有線程,在綠燈的時候,一次性放行所有的排隊中的線程。

clear:將“Flag”設置爲False,當程序執行wait方法時就會阻塞所有線程。
set:將“Flag”設置爲True,wait方法時便不再阻塞

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

import time
import threading
import random

event = threading.Event()

def lighter():
    count = 0
    event.set() #先設置成綠燈
    while True:
        if count > 5 and count < 10:
            #改成紅燈
            event.clear() #把標誌位清除
            print("\033[41;1m red.....\033[0m")
        elif count > 10:
            event.set() #設置成路燈
            count = 0
        else:
            print("\033[42;1m green \033[0m")
        time.sleep(1)
        count +=1
def car(name):
    while True:
        if event.is_set(): #代表綠燈
            print("[%s] is running " % name)
            time.sleep(2)
        else:
            print("[%s] is waitting....... " % name)
            event.wait()
            print("[%s] green light is on ,start going" % name)

light = threading.Thread(target=lighter,)
light.start()
car1 = threading.Thread(target=car,args=("Tesla",))
car1.start()


隊列

通常而言,隊列是一種先進先出的數據結構,與之對應的是堆棧這種後進先出的結構。但是在python中,它內置了一個queue模塊,它不但提供普通的隊列,還提供一些特殊的隊列

Queue:先進先出隊列

import queue
q = queue.Queue(5)
q.put(11)
q.put(22)
q.put(33)
 
print(q.get())
print(q.get())
print(q.get())


Queue類的參數和方法:

qsize() 獲取當前隊列中元素的個數,也就是隊列的大小

empty() 判斷當前隊列是否爲空,返回True或者False

full() 判斷當前隊列是否已滿,返回True或者False

put(self, block=True, timeout=None)

get(self, block=True, timeout=None)
LifoQueue:後進先出隊列

import queue
q = queue.LifoQueue()
q.put(123)
q.put(456)
print(q.get())


PriorityQueue:優先級隊列

q = queue.PriorityQueue()
q.put((1,"alex1"))
q.put((1,"alex2"))
q.put((1,"alex3"))
q.put((3,"alex3"))
print(q.get())


生產者消費者模型

import time,random

import queue,threading

q = queue.Queue()

def Producer(name):

  count = 0

  while count <20:

    time.sleep(random.randrange(3))

    q.put(count)

    print('Producer %s has produced %s baozi..' %(name, count))

    count +=1

def Consumer(name):

  count = 0

  while count <20:

    time.sleep(random.randrange(4))

    if not q.empty():

        data = q.get()

        print(data)

        print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))

    else:

        print("-----no baozi anymore----")

    count +=1

p1 = threading.Thread(target=Producer, args=('A',))

c1 = threading.Thread(target=Consumer, args=('B',))

p1.start()

c1.start()


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