python多線程、鎖、event事件機制的簡單使用

線程和進程

一、線程共享建立它的進程的地址空間,進程有本身的地址空間

二、線程能夠訪問進程全部的數據,線程能夠相互訪問app

三、線程之間的數據是獨立的ide

四、子進程複製線程的數據函數

五、子進程啓動後是獨立的 ,父進程只能殺掉子進程,而不能進行數據交換ui

六、修改線程中的數據,都是會影響其餘的線程,而對於進程的更改,不會影響子進程線程

threading.Thread

Thread 是threading模塊中最重要的類之一,可使用它來建立線程。有兩種方式來建立線程:一種是經過繼承Thread類,重寫它的run方法;另外一種是建立一個threading.Thread對象,在它的初始化函數(__init__)中將可調用對象做爲參數傳入。

先來看看經過繼承threading.Thread類來建立線程的例子:code

import threading
import time

class MyThread(threading.Thread):
    def __init__(self, arg):
        # super(MyThread, self).__init__() # 新式類繼承原有方法寫法
        threading.Thread.__init__(self)
        self.arg = arg

    def run(self):
        time.sleep(2)
        print(self.arg)

for i in range(10):
    thread = MyThread(i)
    print(thread.name)
    thread.start()

另一種建立線程的方法:對象

import threading
import time

def process(arg):
    time.sleep(2)
    print(arg)

for i in range(10):
    t = threading.Thread(target=process, args=(i,))
    print(t.name)
    t.start()

Thread類還定義瞭如下經常使用方法與屬性:

Thread.getName() 獲取線程名稱
Thread.setName() 設置線程名稱
Thread.name 線程名稱

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

判斷線程是不是激活的(alive)。從調用start()方法啓動線程,到run()方法執行完畢或遇到未處理異常而中斷 這段時間內,線程是激活的進程

Thread.is_alive()
Thread.isAlive()內存

Thread.join([timeout]) 調用Thread.join將會使主調線程堵塞,直到被調用線程運行結束或超時。參數timeout是一個數值類型,表示超時時間,若是未提供該參數,那麼主調線程將一直堵塞到被調線程結束

Python GIL(Global Interpreter Lock)

GIL並非Python的特性,它是在實現Python解析器(CPython)時所引入的一個概念。就比如C++是一套語言(語法)標準,可是能夠用不一樣的編譯器來編譯成可執行代碼。有名的編譯器例如GCC,INTEL C++,Visual C++等。Python也同樣,一樣一段代碼能夠經過CPython,PyPy,Psyco等不一樣的Python執行環境來執行。像其中的JPython就沒有GIL。然而由於CPython是大部分環境下默認的Python執行環境。因此在不少人的概念裏CPython就是Python,也就想固然的把GIL歸結爲Python語言的缺陷。因此這裏要先明確一點:GIL並非Python的特性,Python徹底能夠不依賴於GIL。

線程鎖的使用:

# 鎖:GIL 全局解釋器 它是爲了保證線程在運行過程當中不被搶佔
number = 0
lock = threading.RLock()    # 建立鎖


def run(num):
    lock.acquire()  # 加鎖
    global number
    number += 1
    print(number)
    time.sleep(2)
    lock.release()  # 釋放鎖

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

Join & Daemon

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

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

    def run(self):
        print("thread start")
        time.sleep(3)
        print('thread end')

print('main start')
thread1 = MyThread1()
# thread1.setDaemon(True)     # 設置子線程是否跟隨主線程一塊兒結束
thread1.start()
time.sleep(1)
print('satrt join')
# thread1.join()    # 使主線程阻塞,直至子線程運行完畢再繼續主線程
print('end join')
def run(n):
    print('[%s]------running----\n' % n)
    time.sleep(2)
    print('--done--')


def main():
    for i in range(5):
        t = threading.Thread(target=run, args=[i,])
        t.start()
        # t.join()
        print('starting thread', t.getName())


m = threading.Thread(target=main,args=[])
# m.setDaemon(True)  # 將主線程設置爲Daemon線程,它退出時,其它子線程會同時退出,不論是否執行完任務
m.start()
# m.join()    # 使主線程阻塞,直至子線程運行完畢再繼續主線程
print("---main thread done----")

線程鎖(互斥鎖Mutex)

一個進程下能夠啓動多個線程,多個線程共享父進程的內存空間,也就意味着每一個線程能夠訪問同一份數據,此時,若是2個線程同時要修改同一份數據,會出現什麼情況?

num = 100  # 設定一個共享變量

def subNum():
    global num # 在每一個線程中都獲取這個全局變量
    print('--get num:', num)
    time.sleep(2)
    num -= 1 # 對此公共變量進行-1操做

thread_list = []
for i in range(100):
    t = threading.Thread(target=subNum)
    t.start()
    thread_list.append(t)

for t in thread_list: # 等待全部線程執行完畢
    t.join()

print('final num:', num)
# 加鎖版本

def subNum():
    global num  # 在每一個線程中都獲取這個全局變量
    print('--get num:', num)
    time.sleep(1)
    lock.acquire()  # 修改數據前加鎖
    num -= 1  # 對此公共變量進行-1操做
    lock.release()  # 修改後釋放


num = 100  # 設定一個共享變量
thread_list = []
lock = threading.Lock()  # 生成全局鎖
for i in range(100):
    t = threading.Thread(target=subNum)
    t.start()
    thread_list.append(t)

for t in thread_list:  # 等待全部線程執行完畢
    t.join()

print('final num:', num)

Rlock與Lock的區別:

RLock容許在同一線程中被屢次acquire。而Lock卻不容許這種狀況。不然會出現死循環,程序不知道解哪一把鎖。注意:若是使用RLock,那麼acquire和release必須成對出現,即調用了n次acquire,必須調用n次的release才能真正釋放所佔用的鎖

Events

Python提供了Event對象用於線程間通訊,它是由線程設置的信號標誌,若是信號標誌位真,則其餘線程等待直到信號接觸。

Event對象實現了簡單的線程通訊機制,它提供了設置信號,清除信號,等待等用於實現線程間的通訊。

event = threading.Event() 建立一個event

1 設置信號
event.set()

使用Event的set()方法能夠設置Event對象內部的信號標誌爲真。Event對象提供了isSet()方法來判斷其內部信號標誌的狀態。
當使用event對象的set()方法後,isSet()方法返回真

2 清除信號
event.clear()

使用Event對象的clear()方法能夠清除Event對象內部的信號標誌,即將其設爲假,當使用Event的clear方法後,isSet()方法返回假

3 等待
event.wait()

Event對象wait的方法只有在內部信號爲真的時候纔會很快的執行並完成返回。當Event對象的內部信號標誌位假時,
則wait方法一直等待到其爲真時才返回。也就是說必須set新號標誌位真

def do(event):
    print('start')
    event.wait()
    print('execute')

event_obj = threading.Event()
for i in range(10):
    t = threading.Thread(target=do, args=(event_obj,))
    t.start()

event_obj.clear()
inp = input('輸入內容:')
if inp == 'true':
    event_obj.set()
相關文章
相關標籤/搜索