首頁 >後端開發 >Python教學 >詳解python多線程、鎖、event事件機制的簡單使用

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

不言
不言原創
2018-04-27 11:52:121562瀏覽

這篇文章主要介紹了詳解python多線程、鎖、event事件機制的簡單使用,現在分享給大家,也給大家做個參考。一起來看看吧

線程和進程

1、線程共享創建它的進程的地址空間,進程有自己的位址空間

2、執行緒可以存取進程所有的數據,執行緒可以互相存取

3、執行緒之間的數據是獨立的

4、子程序複製執行緒的資料

5、子程序啟動後是獨立的,父行程只能殺掉子程序,而不能進行資料交換

6、修改執行緒中的數據,都是會影響其他的線程,而對於行程的更改,不會影響子程序

threading.Thread

Thread 是threading模組中最重要的類別之一,可以使用它來建立線程。有兩種方式來創建線程:一種是透過繼承Thread類,重寫它的run方法;另一種是創建一個threading.Thread對象,在它的初始化函數(__init__)中將可調用對像作為參數傳入。
先來看看透過繼承threading.Thread類別來建立執行緒的範例:

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()

相关推荐:

Python多线程中阻塞(join)与锁(Lock)使用误区解析

python多线程之事件Event的使用详解

以上是詳解python多線程、鎖、event事件機制的簡單使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn