進程線程

 

進程與線程之間的關係

線程是屬於進程的,線程運行在進程空間內,同一進程所產生的線程共享同一內存空間,當進程退出時該進程所產生的線程都會被強制退出並清除。線程可與屬於同一進程的其它線程共享進程所擁有的所有資源,可是其自己基本上不擁有系統資源,只擁有一點在運行中必不可少的信息(如程序計數器、一組寄存器和棧)。html

 

進程就是一個程序在一個數據集上的一次動態執行過程。 進程通常由程序、數據集、進程控制塊三部分組成。咱們編寫的程序用來描述進程要完成哪些功能以及如何完成;數據集則是程序在執行過程當中所須要使用的資源;進程控制塊用來記錄進程的外部特徵,描述進程的執行變化過程,系統能夠利用它來控制和管理進程,它是系統感知進程存在的惟一標誌。python

 

 

進程和線程的目的:提升執行效率git


一、單進程單線程,主進程、主線程
二、自定義線程:
  主進程
    主線程
    子線程github

 

建立多進程,目的是爲了利用CPU的多核,讓CPU同時執行多個任務。編程

進程:
優勢:同時利用多個CPU,可以同時進行多個操做
缺點:耗費資源(建立時從新開闢內存空間)api

線程:
優勢:共享同一進程內存,IO操做時,不依賴CPU,可併發操做
缺點:搶佔資源瀏覽器

進程、線程越多越好嗎?
進程不是越多越好,通常而言,CPU個數 = 進程個數
線程數,依賴任務而定,不是越多越好,每次記錄線程的請求信息,上下文切換耗時。安全


IO密集型操做(不用CPU):多線程
計算密集型操做(使用CPU):多進程網絡

 

python的進程上有個GIL 全局解釋性鎖,這個會形成,一個進程的多個線程,不能同時使用多個cpu,而是cpu每次只能選一個線程執行,所以,多線程在cpu執行的是無效的。可是在I/O操做的時候是能夠同步的,好比time.sleep就是io 操做,多線程,能夠同時等待多線程

 

  主線程

    好比咱們寫的py文件,執行的時候,全部代碼是如何向下執行呢?確定有個主線程的。

    再咱們建立多線程時候,這些線程都是子線程,那確定有個主線程的。

 

進程和程序關係
  進程:程序實例,程序子集,有所謂生命週期,能夠kill掉,好比你安裝的word,是一個程序,你打開一個文檔是一個進程,能夠關掉。
  進程要想完成併發執行的功能,就要進程切換。進程切換,上下文切換,運行進程,說明在cpu的寄存器裏面有數據了。

 

 

1、Python的多線程或多進程概述

 

Python的多線程或多進程的調度是經過操做系統的調度程序實現的。當一個線程或進程阻塞,例如等待IO時,該線程或進程被操做系統下降執行的優先級,CPU內核能夠被分配用來執行其它有實際計算任務的線程或進程。


Python的線程或進程架構:
「Multi-threading」

 

線程存在於進程以內。一個進程能夠包含多個線程,但一般包含至少一個線程,這個線程被稱爲主線程。在一個進程內的線程共享進程的內存,因此進程內的不一樣線程的通訊能夠經過引用共享的對象來實現。不一樣的進程並不共享同一塊內存,因此進程間的通訊是經過其它接口如文件、sockets或特別分配的共享內存區域來實現的。


當線程須要執行操做時,它請求操做系統的線程調度程序給其分配一些CPU時間。調度程序根據各類參數來將CPU的核心分配給等待的線程,調度程序的實現根據操做系統的不一樣而不一樣。同一個進程中運行的不一樣線程可能同時運行在不一樣的CPU核心上(但CPython例外)。

 

Python的線程和GIL

Python的CPython解釋器包含一個全局解釋器鎖Global Interpreter Lock(GIL),它的存在確保了Python進程中同時只有一個線程能夠執行,即便有多個CPU核心可用。因此CPython程序中的多線程並不能經過多個cpu核心並行執行。不過,即便是這樣,在等待I/O時被阻塞的線程仍然被操做系統下降執行優先級並放入背景等待,以便讓真正有計算任務的線程能夠執行,下圖簡單地描述了這個過程:


「Threading-GIL」

 

上圖中的Waiting for GIL狀態是某個線程已經完成了I/O,在退出阻塞狀態想要開始執行時,另一個線程持有GIL,因此已經就緒的線程被強制等待。在不少的網絡應用程序中,花在等待I/O上的時間比實際處理數據的時間要多得多。只要不是有很是大的併發鏈接數,由GIL致使的鏈接的線程的阻塞是相對較低的,因此對於這些網絡服務程序,使用線程的方式實現併發鏈接,仍然是一個合適的架構。

 

Python的進程與線程的比較

對於操做系統來講,一個應用就是一個進程。好比打開一個瀏覽器,它是一個進程;打開一個記事本,它是一個進程。每一個進程有它特定的進程號。他們共享操做系統的內存資源。進程是操做系統分配資源的最小單位。


而對於每個進程而言,好比一個視頻播放器,它必須同時播放視頻和音頻,就至少須要同時運行兩個「子任務」,進程內的這些子任務就是經過線程來完成。線程是計算機執行任務的最小單元。一個進程它能夠包含多個線程,這些線程相互獨立,同時又共享進程所擁有的資源。


以使用OS資源的角度來講,進程比線程更加「重量級」,建立一個新的進程須要的時間比建立一個新線程來講要多,並且進程使用更多的內存資源。


有一點須要注意的是,若是你須要執行一個計算任務密集的Python程序,最好是經過多進程來實現。由於若是程序中的每一個線程都有繁重的計算任務,它們都要使用CPU,而因爲GIL的存在,這些線程並不能真正的在不一樣的CPU核心上並行執行,因此這會嚴重下降程序總體的性能。

 

 

Python多進程、多線程編程

 

 Python中的多線程通常是經過其內置的threading模塊提供的接口來實現的,多進程是經過multiprocessing模塊實現的。本篇的代碼實例是基於Python3的。


Python多進程編程

 

Process類

在Python中,經過multiprocessing模塊內的Process類來建立新的進程。例如:

 

from multiprocessing import Process
import os

def info(title):
    print(title)
    print('module name:', __name__)
    if hasattr(os, 'getppid'):  # only available on Unix
        print('parent process:', os.getppid())
    print('process id:', os.getpid())

def f(name):
    info('function f')
    print('hello', name)
    
if __name__ == '__main__':
    info('main line')
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()
    
"""
main line
module name: __main__
parent process: 2128
process id: 3688
function f
module name: __mp_main__
parent process: 3688
process id: 6884
hello bob
"""
View Code

 

經過Process類中的start方法來啓動新的進程。有三種不一樣的啓動方式:spawn,fork和forkserver。這三種方式的詳細區別請參考官方文檔

 

Process類中的join方法的做用是等待子進程結束後再繼續往下執行。

 

進程間通訊

multiprocessing模塊內包括兩種進程間通訊的方式:Queues和Pipes;同時也提供了其它方式在進程間共享狀態,不過官方文檔也提到在併發程序設計中儘可能避免這種方式。


先來看一下Queues的例子:

 

from multiprocessing import Process, Queue
def f(q):
    q.put([42, None, 'hello'])
if __name__ == '__main__':
    q = Queue()
    p = Process(target=f, args=(q,))
    p.start()
    print(q.get())    # prints "[42, None, 'hello']"
    p.join()
    
"""
[42, None, 'hello']
"""
View Code

 

multiprocessing模塊中的Queue幾乎是queue.Queue的克隆,而且Queues是線程和進程安全的,這意味着多個線程或進程訪問同一個Queue實例時,不會出現數據混亂的情況。下面來看一個使用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,))
    p.start()
    print(parent_conn.recv())   # prints "[42, None, 'hello']"
    p.join()
    
"""
[42, None, 'hello']
"""
View Code

 

Pipe方法返回一對鏈接對象,表明管道的兩端,每一個鏈接對象都有send()和recv()方法。若是不一樣的進程(或線程)在同一時刻試圖在管道的同一端進行讀寫,數據可能會出現混亂,在不一樣端是沒有問題的。Python中的對象大部分都不是進程(或線程)安全的,因此在不一樣的進程(或線程)操做同一對象時,須要使用同步機制。

 

進程的同步機制

進程的同步是經過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()

"""
hello world 1
hello world 3
hello world 0
hello world 2
hello world 7
hello world 6
hello world 4
hello world 8
hello world 9
hello world 5
"""
View Code

 

使用進程池

Pool類提供了一個批量建立多個子進程的方法,能夠給定子進程數的上限,避免無限地消耗系統的資源。看一下官方文檔的實例:

 

from multiprocessing import Pool, TimeoutError
import time
import os
def f(x):
    return x*x
if __name__ == '__main__':
    # start 4 worker processes
    with Pool(processes=4) as pool:
        # print "[0, 1, 4,..., 81]"
        print(pool.map(f, range(10)))
        # print same numbers in arbitrary order
        for i in pool.imap_unordered(f, range(10)):
            print(i)
        # evaluate "f(20)" asynchronously
        res = pool.apply_async(f, (20,))      # runs in *only* one process
        print(res.get(timeout=1))             # prints "400"
        # evaluate "os.getpid()" asynchronously
        res = pool.apply_async(os.getpid, ()) # runs in *only* one process
        print(res.get(timeout=1))             # prints the PID of that process
        # launching multiple evaluations asynchronously *may* use more processes
        multiple_results = [pool.apply_async(os.getpid, ()) for i in range(4)]
        print([res.get(timeout=1) for res in multiple_results])
        # make a single worker sleep for 10 secs
        res = pool.apply_async(time.sleep, (10,))
        try:
            print(res.get(timeout=1))
        except TimeoutError:
            print("We lacked patience and got a multiprocessing.TimeoutError")
        print("For the moment, the pool remains available for more work")
    # exiting the 'with'-block has stopped the pool
    print("Now the pool is closed and no longer available")
    
    """
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
0
1
4
9
16
25
36
49
64
81
400
8652
[8652, 8652, 8652, 8652]
We lacked patience and got a multiprocessing.TimeoutError
For the moment, the pool remains available for more work
Now the pool is closed and no longer available
    """
View Code

 

Python多線程編程

Python中使用線程的方式有不少都與使用進程的方式相似,這裏就再也不列出實例代碼。

 

Thread類

在Python中,經過threading模塊內的Thread類來建立新的進程。經過Thread類中的start方法啓動線程,join方法等待線程結束。

 

線程間通訊

線程間最簡單的通訊方式是經過threading模塊內的Event類。固然也能夠經過共享對象來在線程間共享狀態信息。因爲queue.Queue是線程和進程安全的,因此它也是線程通訊使用理想對象。其它對象大部分都不是線程(或進程)安全的,因此在不一樣的線程(或進程)操做同一對象時,須要使用同步機制。

 

線程的同步機制

threading模塊中提供了Lock,RLock,Condition,Semaphore等類進行線程間的同步。

關於threading模塊中更詳細的使用信息,請參考官方文檔

 

內容來源與參考:

https://andyyoung01.github.io/2017/01/21/Python%E7%9A%84%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B8%8E%E5%A4%9A%E8%BF%9B%E7%A8%8B%E7%AE%80%E4%BB%8B-%E4%B8%8A/#Python的線程和GIL

 

https://andyyoung01.github.io/2017/01/22/Python%E7%9A%84%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B8%8E%E5%A4%9A%E8%BF%9B%E7%A8%8B%E7%AE%80%E4%BB%8B-%E4%B8%8B/

 

 

 

Python內的線程進程協程模塊實現

 

threading模塊

threading 模塊創建在 _thread 模塊之上。thread 模塊以低級、原始的方式來處理和控制線程,而 threading 模塊經過對 thread 進行二次封裝,提供了更方便的 api 來處理線程。

threading基於Java的線程模型設計。鎖(Lock)和條件變量(Condition)在Java中是對象的基本行爲(每個對象都自帶了鎖和條件變量),而在Python中則是獨立的對象,因此python的threading模塊中還提供了Lock,Rlock,Condition,Event等經常使用類,它們在python中是獨立於Tread模塊的,可是卻與線程緊密相關,不可分割。

須要注意的是:python的線程中沒有優先級、線程組,也不能被中止、暫停、恢復、中斷,線程只能隨着線程中的代碼執行完畢而被銷燬。在實現線程池的時候沒法中止已經注入了方法且執行超時的線程。

 

建立線程的兩種方式

第一種建立線程的方式 建立5個線程

import threading
import time

def worker(num):
    time.sleep(3)
    print("Thread %d" % num)
    return

for i in range(5):
    t = threading.Thread(target=worker, args=(i,))
    t.start()
    
"""
Thread 0
Thread 1
Thread 2
Thread 4
Thread 3
"""
View Code

 

第二種建立線程的方式 建立5個線程

import threading
import time

class MyThread(threading.Thread):
    def __init__(self, name):
        # threading.Thread.__init__(self)
        super(MyThread, self).__init__(target=self.fun, name="t %d" % i)
        self.name = name

    def fun(self):
        time.sleep(2)
        print("name %s thread %s" % (self.name, threading.current_thread().name))

for i in range(5):
    t = MyThread(i)
    t.start()
    
"""
name 0 thread 0
name 2 thread 2
name 1 thread 1
name 4 thread 4
name 3 thread 3
"""
View Code

 

thread線程類的方法說明

t.start() : 激活線程,

t.getName() : 獲取線程的名稱

t.setName() : 設置線程的名稱 

t.name : 獲取或設置線程的名稱

t.is_alive() : 判斷線程是否爲激活狀態

t.isAlive() :判斷線程是否爲激活狀態

t.setDaemon() 設置爲後臺線程或前臺線程(默認:False);經過一個布爾值設置線程是否爲守護線程,必須在執行start()方法以後纔可使用。若是是後臺線程,主線程執行過程當中,後臺線程也在進行,主線程執行完畢後,後臺線程不論成功與否,均中止;若是是前臺線程,主線程執行過程當中,前臺線程也在進行,主線程執行完畢後,等待前臺線程也執行完成後,程序中止

t.isDaemon() : 判斷是否爲守護線程

t.ident :獲取線程的標識符。線程標識符是一個非零整數,只有在調用了start()方法以後該屬性纔有效,不然它只返回None。

t.join() :逐個執行每一個線程,執行完畢後繼續往下執行,該方法使得多線程變得無心義

t.run() :線程被cpu調度後自動執行線程對象的run方法

 

join 代碼 

import time
import threading

def printNum(a):
    print('num:',a)
    time.sleep(1)

def ThreadTest(i):
    return threading.Thread(target=printNum, args=(999,))

thread_arr = []
for i in range(5):
    t = ThreadTest(i)
    thread_arr.append(t)

for t in thread_arr:
    t.start()

for t in thread_arr:
    t.join()

print('finished')

"""
num: 999
num: 999
num: 999
num: 999
num: 999
finished
"""
View Code

 

 

多線程的說明

threading用於提供線程相關的操做,線程是應用程序中工做的最小單元。python當前版本的多線程庫沒有實現優先級、線程組,線程也不能被中止、暫停、恢復、中斷。

threading模塊提供的類:  
  Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, local。

threading 模塊提供的經常使用方法: 
  threading.currentThread(): 返回當前的線程變量。 
  threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啓動後、結束前,不包括啓動前和終止後的線程。 
  threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果。

threading 模塊提供的常量:

  threading.TIMEOUT_MAX 設置threading全局超時時間。

 

Thread是線程類,有兩種使用方法,直接傳入要運行的方法或從Thread繼承並覆蓋run():

import threading
import time

# 方法一:將要執行的方法做爲參數傳給Thread的構造方法
def action(arg):
    time.sleep(1)
    print('the arg is:%s\r' % arg)

for i in range(4):
    t = threading.Thread(target=action, args=(i,))
    t.start()

print('main thread end!')

"""
main thread end!
the arg is:0
the arg is:3
"""


# 方法二:從Thread繼承,並重寫run()
class MyThread(threading.Thread):
    def __init__(self, arg):
        super(MyThread, self).__init__()  # 注意:必定要顯式的調用父類的初始化函數。
        self.arg = arg

    def run(self):  # 定義每一個線程要運行的函數
        time.sleep(1)
        print('the arg is:%s\r' % self.arg)

for i in range(4):
    t = MyThread(i)
    t.start()

print('main thread end!')

"""
main thread end!
the arg is:1

the arg is:2
the arg is:3
"""
View Code

 

 

Lock、Rlock類

 

因爲線程之間隨機調度:某線程可能在執行n條後,CPU接着執行其餘線程。爲了多個線程同時操做一個內存中的資源時不產生混亂,咱們使用鎖。

 

Lock(指令鎖)是可用的最低級的同步指令。Lock處於鎖定狀態時,不被特定的線程擁有。Lock包含兩種狀態——鎖定和非鎖定,以及兩個基本的方法。

能夠認爲Lock有一個鎖定池,當線程請求鎖定時,將線程置於池中,直到得到鎖定後出池。池中的線程處於狀態圖中的同步阻塞狀態。

 

RLock(可重入鎖)是一個能夠被同一個線程請求屢次的同步指令。RLock使用了「擁有的線程」和「遞歸等級」的概念,處於鎖定狀態時,RLock被某個線程擁有。擁有RLock的線程能夠再次調用acquire(),釋放鎖時須要調用release()相同次數。

能夠認爲RLock包含一個鎖定池和一個初始值爲0的計數器,每次成功調用 acquire()/release(),計數器將+1/-1,爲0時鎖處於未鎖定狀態。

 

簡言之:Lock屬於全局,Rlock屬於線程。

 

構造方法: 
Lock(),Rlock(),推薦使用Rlock()

實例方法: 
  acquire([timeout]): 嘗試得到鎖定。使線程進入同步阻塞狀態。 
  release(): 釋放鎖。使用前線程必須已得到鎖定,不然將拋出異常。

 

線程鎖threading.RLock和threading.Lock

咱們使用線程對數據進行操做的時候,若是多個線程同時修改某個數據,可能會出現不可預料的結果,爲了保證數據的準確性,引入了鎖的概念。

例:假設列表A的全部元素就爲0,當一個線程從前向後打印列表的全部元素,另一個線程則從後向前修改列表的元素爲1,那麼輸出的時候,列表的元素就會一部分爲0,一部分爲1,這就致使了數據的不一致。鎖的出現解決了這個問題。

 View Code

 

 Lock與Rlock 對比

import threading   
lock = threading.Lock() #Lock對象   
lock.acquire()   
lock.acquire()  #產生了死鎖。   
lock.release()   
lock.release()   


import threading   
rLock = threading.RLock()  #RLock對象   
rLock.acquire()   
rLock.acquire() #在同一線程內,程序不會堵塞。   
rLock.release()   
rLock.release()   

lock vs rlock Code
View Code

 

未使用鎖

屢次運行可能產生混亂。這種場景就是適合使用鎖的場景。

import threading
import time

gl_num = 0

def show(arg):
    global gl_num
    time.sleep(0.00001)
    gl_num += 1
    print(gl_num)

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

print('main thread stop')

"""
1
2
3
main thread stop
4
5
"""
View Code

 

使用鎖

全局變量在每次被調用時都要得到鎖,才能操做,所以保證了共享數據的安全性。

import threading
import time

gl_num = 0

lock = threading.RLock()

# 調用acquire([timeout])時,線程將一直阻塞,直到得到鎖定或者直到timeout秒後(timeout參數可選)。
# 返回是否得到鎖。
def Func():
    lock.acquire()
    global gl_num
    gl_num += 1
    time.sleep(0.00001)
    print(gl_num)
    lock.release()

for i in range(5):
    t = threading.Thread(target=Func)
    t.start()

"""
1
2
3
4
5
"""
View Code

 

Condition類


條件變量對象能讓一個線程停下來,等待其它線程知足了某個「條件」。如,狀態的改變或值的改變。

Condition(條件變量)一般與一個鎖關聯。須要在多個Contidion中共享一個鎖時,能夠傳遞一個Lock/RLock實例給構造方法,不然它將本身生成一個RLock實例。

能夠認爲,除了Lock帶有的鎖定池外,Condition還包含一個等待池,池中的線程處於等待阻塞狀態,直到另外一個線程調用notify()/notifyAll()通知;獲得通知後線程進入鎖定池等待鎖定。

 

一個condition變量老是與某些類型的鎖相聯繫,這個可使用默認的狀況或建立一個,當幾個condition變量必須共享和同一個鎖的時候,是頗有用的。鎖是conditon對象的一部分:沒有必要分別跟蹤。

condition變量服從上下文管理協議:with語句塊封閉以前能夠獲取與鎖的聯繫。 acquire() 和 release() 會調用與鎖相關聯的相應的方法。

其餘和鎖關聯的方法必須被調用,wait()方法會釋放鎖,當另一個線程使用 notify() or notify_all()喚醒它以前會一直阻塞。一旦被喚醒,wait()會從新得到鎖並返回

Condition(lock=None)

 

 

構造方法: 
Condition([lock/rlock])

 

實例方法: 

  • acquire([timeout])/release(): 調用關聯的鎖的相應方法,給線程上鎖/解鎖。 
  • wait([timeout]): 調用這個方法將使線程進入Condition的等待池等待通知,並釋放鎖。使用前線程必須已得到鎖定,不然將拋出異常。 
  • notify(): 調用這個方法將從等待池挑選一個線程並通知,收到通知的線程將自動調用acquire()嘗試得到鎖定(進入鎖定池);其餘線程仍然在等待池中。調用這個方法不會釋放鎖定。使用前線程必須已得到鎖定,不然將拋出異常。 
  • notifyAll(): 調用這個方法將通知等待池中全部的線程,這些線程都將進入鎖定池嘗試得到鎖定。調用這個方法不會釋放鎖定。使用前線程必須已得到鎖定,不然將拋出異常。

 

比較經典的例子是生產者與消費者模型,代碼中寫了兩個類,Consumer和Producer,分別繼承了Thread類,分別初始化這兩個類得到了c和p對象,並啓動這兩個線程。則這兩個線程去執行run方法(這裏與Thread類內部的調度有關),定義了producer全局變量和condition對象爲全局變量,當producer不大於1時,消費者線程被condition對象阻塞,不能繼續消費(這裏是再也不遞減),當producer不小於10時,生產者線程被condition對象阻塞,再也不生產(這裏是再也不累加),代碼在下面,拿去執行,斷點一下就明白了。

import threading
import time

condition = threading.Condition()
products = 0

class Producer(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        global condition, products
        while True:
            if condition.acquire():
                if products < 10:
                    products += 1;
                    print("Producer(%s):deliver one, now products:%s" %(self.name, products))
                    condition.notify()
                else:
                    print("Producer(%s):already 10, stop deliver, now products:%s" %(self.name, products))
                    condition.wait();
                condition.release()
                time.sleep(2)

class Consumer(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        global condition, products
        while True:
            if condition.acquire():
                if products > 1:
                    products -= 1
                    print("Consumer(%s):consume one, now products:%s" %(self.name, products))
                    condition.notify()
                else:
                    print("Consumer(%s):only 1, stop consume, products:%s" %(self.name, products))
                    condition.wait();
                condition.release()
                time.sleep(2)

if __name__ == "__main__":
    for p in range(0, 2):
        p = Producer()
        p.start()

    for c in range(0, 10):
        c = Consumer()
        c.start()

"""
Producer(Thread-1):deliver one, now products:1
Producer(Thread-2):deliver one, now products:2
Consumer(Thread-3):consume one, now products:1
Consumer(Thread-4):only 1, stop consume, products:1
Consumer(Thread-5):only 1, stop consume, products:1
.....
"""
View Code

 

Event類


通用的條件變量。多個線程能夠等待某個事件的發生,在事件發生後,全部的線程都會被激活。

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

Event其實就是一個簡化版的 Condition。Event沒有鎖,沒法使線程進入同步阻塞狀態。

 

構造方法: 
Event()

 

實例方法: 

  • isSet(): 當內置標誌爲True時返回True。 
  • set(): 將標誌設爲True,並通知全部處於等待阻塞狀態的線程恢復運行狀態。 
  • clear(): 將標誌設爲False。 
  • wait([timeout]): 若是標誌爲True將當即返回,不然阻塞線程至等待阻塞狀態,等待其餘線程調用set()。

這是一個比較關鍵的類,它的意義在於能夠控制屬於同一個線程類的多個實例化對象,讓他們同時阻塞或者執行。配合隊列來實現一個線程池很是好用。

 

!!線程之間交互  threading.Event方法  紅燈 綠燈  信號標誌位!!

 

import threading
import time
import random

def light():
    if not event.isSet():  #沒有設置的話
        event.set()  # 設置綠燈
    count = 0  #計數器秒數
    while True:
        if count < 10:    #小於十秒 是綠燈
            print("\033[42;1m ------green light on ----\033[0m")
        elif count < 13:  #小於13秒 大於10秒 是黃燈
            print("\033[43;1m ------yellow light on ----\033[0m")
        elif count < 20:  #小於於20秒 有設置則取消
            if event.isSet():
                event.clear()
            print("\033[41;1m ------red light on ----\033[0m")
        else:  #大於20 從新
            count = 0    #取消秒數計時
            event.set()   #從新變爲綠燈

        time.sleep(1)
        count +=1

def car(n):  # 第二個線程 車線程
    while 1:
        time.sleep(random.randrange(3))  #隨機等待三秒
        if event.isSet():
            print("car [%s] is running..." % n)  #若是被設置了信號則是綠燈,該線程的車便可經過
        else:  #不然的話提示紅燈
            print("car [%s] is waitting for the red light.." %n)
            event.wait()   #紅燈的話,會在此處卡住,不往下執行
            print("Green light is on ,car %s is running......." %n)
if __name__ == '__main__':  #下面是定義了兩個線程  ,燈線程 車線程, threading.Event用來設置標着符號讓兩個線程交流
    event = threading.Event()
    Light = threading.Thread(target=light)
    Light.start()
    for i in range(3):
        t = threading.Thread(target=car,args=(i,))
        t.start()
"""
 ------green light on ----
car [2] is running...
car [0] is running...
 ------yellow light on ----
car [1] is running...
car [1] is running...
car [2] is running...
car [0] is running...
 ------red light on ----
car [1] is waitting for the red light..
......
"""
View Code

 

 

queue模塊

Queue 就是對隊列,它是線程安全的

舉例來講,咱們去肯德基吃飯。廚房是給咱們作飯的地方,前臺負責把廚房作好的飯賣給顧客,顧客則去前臺領取作好的飯。這裏的前臺就至關於咱們的隊列。

這個模型也叫生產者-消費者模型。

 

import queue
 
q = queue.Queue(maxsize=0)  # 構造一個先進先出隊列,maxsize指定隊列長度,爲0 時,表示隊列長度無限制。
 
q.join()    # 等到隊列爲空的時候,再執行別的操做
q.qsize()   # 返回隊列的大小 (不可靠)
q.empty()   # 當隊列爲空的時候,返回True 不然返回False (不可靠)
q.full()    # 當隊列滿的時候,返回True,不然返回False (不可靠)

q.put(item, block=True, timeout=None) #  將item放入Queue尾部,item必須存在,能夠參數block默認爲True,表示當隊列滿時,會等待隊列給出可用位置。爲False時爲非阻塞,此時若是隊列已滿,會引起queue.Full 異常。 可選參數timeout,表示 會阻塞設置的時間,事後,若是隊列沒法給出放入item的位置,則引起 queue.Full 異常

q.get(block=True, timeout=None) #   移除並返回隊列頭部的一個值,可選參數block默認爲True,表示獲取值的時候,若是隊列爲空,則阻塞,爲False時,不阻塞,若此時隊列爲空,則引起 queue.Empty異常。 可選參數timeout,表示會阻塞設置的時候,事後,若是隊列爲空,則引起Empty異常。

q.put_nowait(item) #   等效於 put(item,block=False)
q.get_nowait() #    等效於 get(item,block=False)
View Code
相關文章
相關標籤/搜索