python線程

全局解釋器鎖GIL:python

python代碼的執行由python虛擬機來控制,python在設計之初就考慮到要在主循環中,同時只有只有一個線程來執行。程序員

在多線環境中,python虛擬機按如下方式執行:編程

1,設置GIL;數據結構

2,切換到一個線程去運行;多線程

3,運行指定數量的字節代碼指令或者線程主動讓出控制(timg.sleep);異步

4,把線程設置成睡眠狀態;ide

5,解鎖GIL;ui

6,重複以上步驟spa

Python提供了幾個用於多線程編程的模塊,包括thread、threading和Queue等。thread和threading模塊容許程序員建立和管理線程。thread模塊提供了基本的線程和鎖的支持,threading提供了更高級別、功能更強的線程管理的功能。Queue模塊容許用戶建立一個能夠用於多個線程之間共享數據的隊列數據結構。操作系統

Threading用於提供線程相關的操做,線程是CPU調度的最小單位。 

import threading
import time
  
def show(arg):
    time.sleep(1)
    print'thread'+str(arg))
  
for i in range(10):
    t = threading.Thread(target=show, args=(i,))
    t.start()
  
print'main thread stop'

上述代碼建立了10個線程,而後等待cpu調度,分片執行指令。

import threading
class MyThread(threading.Thread):
    def __init__(self,n):
        threading.Thread.__init__(self)  #或者 super().__init__()
        self.n=n
    def run(self):
        print(self.n)

t1=MyThread(1)
t1.start()
print('主線程1')
t2=MyThread(2)
t2.start()
print('主線程2')
自定義線程
更多方法:

  start             線程準備就緒,等待CPU調度

  setName      爲線程設置名稱

  getName    獲取線程名稱

  run                線程被cpu調度後,自動執行線程對象的run方法

  join():         主線程A中,建立了子線程B,而且在主線程A中調用了B.join(),那麼,主線程A會在調用的地方等待,直到子線程B完成操做後,

         才能夠接着往下執行,那麼在調用這個線程時可使用被調用線程的join方法。  

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

 

threading模塊提供的一些方法:

  isAlive(): 返回線程是否活動的。 threading.currentThread(): 返回當前的線程變量。 threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啓動後、結束前,不包括啓動前和終止後的線程。 threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果。 

import time

def work():
    time.sleep(1)
    print(threading.current_thread().getName())

if __name__ == '__main__':
    #在主進程下開啓線程
    t=threading.Thread(target=work)
    t.start()
    print(threading.current_thread().getName())
    print(threading.current_thread()) #主線程
    print(threading.enumerate()) #連同主線程在內有兩個運行的線程
    print(threading.active_count())
    print('主線程/主進程')
>>>
MainThread
<_MainThread(MainThread, started 4048)>
[<_MainThread(MainThread, started 4048)>, <Thread(Thread-1, started 2664)>]
2
主線程/主進程
Thread-1
方法實例 

 

from threading import Thread
import time
def task(n):
    time.sleep(1)
    print(n)

for i in range(3):
    t=Thread(target=task,args=(i,))
    t.start()
    t.join()
    print('主線程')
    print(t.is_alive())
>>>

0
主線程
False
1
主線程
False
2
主線程
False
join

   鎖                                                                                                                                   

線程鎖(Lock/Rlock)

 因爲線程之間是隨機調度,而且每一個線程執行指定字節或者時間,以後其它線程修改同一條數據時可能出現髒數據,因此出現了線程鎖。

同步鎖:

import threading
import time

n=0
lock=threading.Lock()
def task():
    global n
    lock.acquire()
    n += 1
    time.sleep(1)
    print(n)
    lock.release()
for i in range(10):
    t=threading.Thread(target=task)
    t.start()
輸出結果:1-10
若是不加鎖,當程序停1秒的時候,n+1已經運行了十遍,即n=10.
VLock

遞歸鎖:

import threading
import time

n=0
lock=threading.RLock()
def task():
    global n
    lock.acquire()
    n += 1
    lock.acquire()
    n+=1
    time.sleep(1)
    print(n)
    lock.release()
    lock.release()
for i in range(10):
    t=threading.Thread(target=task)
    t.start()
輸出結果:2,4,6,8,10,12,14,16,18,20
Rlock
python中爲了支持同一線程中屢次請求同一資源,提供了遞歸鎖Rlock,Rlock內部維護着一個Lock和一個Counter變量,counter記錄了acquire的次數,從而使得資源能夠屢次被require。直到一個線程的acquire都被release,其它線程才能得到資源。若是同步鎖採用屢次require,就會產生死鎖。

 信號量:

  --  同時容許必定數量的線程更改數據.

import threading, time

def run(n):
    semaphore.acquire()
    time.sleep(1)
    print(n)
    semaphore.release()


semaphore= threading.BoundedSemaphore(5)  # 最多容許5個線程同時運行
for i in range(10):
    t = threading.Thread(target=run, args=(i,))
    t.start()
semaphore

事件:

  --  python線程的事件用於主線程控制其餘線程的執行

  event.isSet():返回event的狀態值;   event.wait():若是 event.isSet()==False將阻塞線程;   event.set(): 設置event的狀態值爲True,全部阻塞池的線程激活進入就緒狀態, 等待操做系統調度;   event.clear():恢復event的狀態值爲False
import threading

def do(event):
    print('start')
    event.wait()    #加鎖:紅燈(False)
    print('execute')
event = threading.Event()
for i in range(5):
    t=threading.Thread(target=do,args=(event,))
    t.start()
input('>>>')
event.set()     #綠燈(True)
event.clear()   #再次變紅燈(False)
for i in range(5):
    t=threading.Thread(target=do,args=(event,))
    t.start()
event.set()
Event

條件:

  使得線程等待,只有知足條件時,才釋放n個線程. 

import threading
lock=threading.Condition()
def task(n):
    lock.acquire()
    lock.wait()
    print(n)
    lock.release()
for i in range(5):
    t=threading.Thread(target=task,args=(i,))
    t.start()
while True:
    inp=input('>>>')    #輸入幾個,就釋放幾個
    if inp == 'q':      #輸入q,就退出,什麼也不執行
        break
    lock.acquire()
    lock.notify(int(inp))
    lock.release()
Condition
def condition_func():

    ret = False
    inp = input('>>>')
    if inp == '1':
        ret = True

    return ret


def run(n):
    con.acquire()
    con.wait_for(condition_func)
    print("run the thread: %s" %n)
    con.release()

if __name__ == '__main__':

    con = threading.Condition()
    for i in range(10):
        t = threading.Thread(target=run, args=(i,))
        t.start()
Condition條件

線程池:

    concurrent.futures模塊提供了高度封裝的異步調用接口

ThreadPoolExecutor:線程池,提供異步調用 
from concurrent.futures import ThreadPoolExecutor

def task(arg):
    print(arg)
pool = ThreadPoolExecutor(5)
for i in range(10):
    pool.submit(task,i)
print('end')
線程池
 
 線程隊列:  

  --  queue隊列

先進先出

import queue

q=queue.Queue()
q.put('first')
q.put('second')
q.put('third')

print(q.get())
print(q.get())
print(q.get())

輸出結果:
first
second
third
示例

後進先出

import queue

q=queue.LifoQueue()
q.put('first')
q.put('second')
q.put('third')

print(q.get())
print(q.get())
print(q.get())

輸出結果:
third
second
first
示例

優先級隊列

import queue

q=queue.PriorityQueue()
#put進入一個元組,元組的第一個元素是優先級(一般是數字,也能夠是非數字之間的比較),數字越小優先級越高
q.put((20,'a'))
q.put((10,'b'))
q.put((30,'c'))

print(q.get())
print(q.get())
print(q.get())

輸出結果(數字越小優先級越高,優先級高的優先出隊):
(10, 'b')
(20, 'a')
(30, 'c')
示例

 threading.local()                           

類的對象.xx='xx' 調用對象的 setattr 方法 (類的對象.[xx]='xx' 調用對象的 __setitem__ 方法) 

import time
import threading
INFO = {}
class Local(object):
    def __getattr__(self, item):
        ident = threading.get_ident()
        return INFO[ident][item]

    def __setattr__(self, key, value):
        ident = threading.get_ident()
        if ident in INFO:
            INFO[ident][key] = value
        else:
            INFO[ident] = {key:value}
obj = Local()
def func(arg):
    obj.phone = arg # 調用對象的 __setattr__方法(「phone」,1)
    time.sleep(1)
    print(obj.phone,arg)

for i in range(10):
    t =threading.Thread(target=func,args=(i,))
    t.start()
原理

 練習題:

class Foo(object):

    def __init__(self):
        object.__setattr__(self, 'info', {}) # 在對象中設置值的本質

    def __setattr__(self, key, value):
        self.info[key] = value

    def __getattr__(self, item):
        print(item)
        return self.info[item]

obj = Foo()
obj.name = 'salah'
print(obj.name)
>>>
name
salah
對象中設置值的本質

 

多線程環境下,每個線程都可以使用所屬進程的全局變量。若是一個線程對全局變量進行了修改,將會影響到其餘全部的線程。只用全局變量並不能知足多線程環境的需求,不少時候線程還須要擁有本身的私有數據,這些數據對於其餘線程來講不可見。所以線程中也可使用局部變量,局部變量只有線程自身能夠訪問,同一個進程下的其餘線程不可訪問。

可是有時候使用局部變量不太方便,所以 python 還提供了 ThreadLocal 變量,它自己是一個全局變量,可是每一個線程卻能夠利用它來保存屬於本身的私有數據,這些私有數據對其餘線程也是不可見的。

import threading

X = 'abc'
ctx = threading.local()
ctx.x = 123  # 主線程中定義x本地屬性
print(ctx, type(ctx), ctx.x)

def work():
    print(X)
    print(ctx)
    print(ctx.x)  # 子線程訪問不到

threading.Thread(target=work).start()

>>>
<_thread._local object at 0x00000276D583C780> <class '_thread._local'> 123
abc
<_thread._local object at 0x00000276D583C780>
示例
相關文章
相關標籤/搜索