溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么在python中實(shí)現(xiàn)多線程

發(fā)布時(shí)間:2021-03-22 17:04:31 來(lái)源:億速云 閱讀:204 作者:Leah 欄目:開(kāi)發(fā)技術(shù)

今天就跟大家聊聊有關(guān)怎么在python中實(shí)現(xiàn)多線程,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

threading 模塊支持守護(hù)線程, 其工作方式是:守護(hù)線程一般是一個(gè)等待客戶端請(qǐng)求服務(wù)的服務(wù)器。

如果把一個(gè)線程設(shè)置為守護(hù)線程,進(jìn)程退出時(shí)不需要等待這個(gè)線程執(zhí)行完成。

如果主線程準(zhǔn)備退出時(shí),不需要等待某些子線程完成,就可以為這些子線程設(shè)置守護(hù)線程標(biāo)記。 需要在啟動(dòng)線程之前執(zhí)行如下賦值語(yǔ)句: thread.daemon = True,檢查線程的守護(hù)狀態(tài)也只需要檢查這個(gè)值即可。

整個(gè) Python 程序?qū)⒃谒蟹鞘刈o(hù)線程退出之后才退出, 換句話說(shuō), 就是沒(méi)有剩下存活的非守護(hù)線程時(shí)才退出。

使用thread模塊

以下是三種使用 Thread 類的方法(一般使用第一個(gè)或第三個(gè)方案)

  • 創(chuàng)建 Thread 的實(shí)例,傳給它一個(gè)函數(shù)。

import threading
from time import sleep, ctime
loops = [3, 2, 1, 1, 1]
def loop(i, nsec):
  print(f'start loop {i} at: {ctime()}')
  sleep(nsec)
  print(f'end loop {i} at: {ctime()}')
def main():
  print('start at', ctime())
  threads = []
  nloops = range(len(loops))
  for i in nloops:
    t = threading.Thread(target=loop, args=(i, loops[i]))
    threads.append(t)
  for i in nloops: # start threads
    threads[i].start()
  for i in nloops: # wait for all
    threads[i].join() # threads to finish
  print(f'all done at: {ctime()}')
if __name__ == '__main__':
  main()

當(dāng)所有線程都分配完成之后,通過(guò)調(diào)用每個(gè)線程的 start()方法讓它們開(kāi)始執(zhí)行,而不是 在這之前就會(huì)執(zhí)行。
相比于管理一組鎖(分配、獲取、釋放、檢查鎖狀態(tài)等)而言,這里只 需要為每個(gè)線程調(diào)用 join()方法即可。
join()方法將等待線程結(jié)束,或者在提供了超時(shí)時(shí)間的情況下,達(dá)到超時(shí)時(shí)間。
使用 join()方法要比等待鎖釋放的無(wú)限循環(huán)更加清晰(這也是這種鎖 又稱為自旋鎖的原因)。

  • 創(chuàng)建 Thread 的實(shí)例,傳給它一個(gè)可調(diào)用的類實(shí)例。

import threading
from time import sleep, ctime
# 創(chuàng)建 Thread 的實(shí)例,傳給它一個(gè)可調(diào)用的類實(shí)例
loops = [3, 2, 1, 1, 1]
class ThreadFunc(object):
  def __init__(self, func, args, name=''):
    self.name = name
    self.func = func
    self.args = args
  def __call__(self):
    self.func(*self.args)
def loop(i, nsec):
  print(f'start loop {i} at: {ctime()}')
  sleep(nsec)
  print(f'end loop {i} at: {ctime()}')
def main():
  print('start at', ctime())
  threads = []
  nloops = range(len(loops))
  for i in nloops:
    t = threading.Thread(target=ThreadFunc(loop, (i, loops[i]), loop.__name__))
    threads.append(t)
  for i in nloops: # start threads
    threads[i].start()
  for i in nloops: # wait for all
    threads[i].join() # threads to finish
  print(f'all done at: {ctime()}')
if __name__ == '__main__':
  main()
  • 派生 Thread 的子類,并創(chuàng)建子類的實(shí)例。

import threading
from time import sleep, ctime
# 創(chuàng)建 Thread 的實(shí)例,傳給它一個(gè)可調(diào)用的類實(shí)例
# 子類的構(gòu)造函數(shù)必須先調(diào)用其基類的構(gòu)造函數(shù)
# 特殊方法__call__()在 子類中必須要寫(xiě)為 run()
loops = [3, 2, 1, 1, 1]
class MyThread(threading.Thread):
  def __init__(self, func, args, name=''):
    threading.Thread.__init__(self)
    self.name = name
    self.func = func
    self.args = args
  def run(self):
    self.func(*self.args)
def loop(i, nsec):
  print(f'start loop {i} at: {ctime()}')
  sleep(nsec)
  print(f'end loop {i} at: {ctime()}')
def main():
  print('start at', ctime())
  threads = []
  nloops = range(len(loops))
  for i in nloops:
    t = MyThread(loop, (i, loops[i]), loop.__name__)
    threads.append(t)
  for i in nloops: # start threads
    threads[i].start()
  for i in nloops: # wait for all
    threads[i].join() # threads to finish
  print(f'all done at: {ctime()}')
if __name__ == '__main__':
  main()

使用鎖

python和java一樣,也具有鎖機(jī)制,而且創(chuàng)建與使用鎖都是很簡(jiǎn)便的。

一般在多線程代碼中,總會(huì)有一些特 定的函數(shù)或代碼塊不希望(或不應(yīng)該)被多個(gè)線程同時(shí)執(zhí)行,通常包括修改數(shù)據(jù)庫(kù)、更新文件或 其他會(huì)產(chǎn)生競(jìng)態(tài)條件的類似情況

鎖有兩種狀態(tài):鎖定和未鎖定。而且它也只支持兩個(gè)函數(shù):獲得鎖和釋放鎖。

一般鎖的調(diào)用如下

# 加載線程的鎖對(duì)象
lock = threading.Lock()
# 獲取鎖
lock.acquire()
# ...代碼
# 釋放鎖
lock.release()

更簡(jiǎn)潔的方法是使用with關(guān)鍵字,如下代碼功能同上

# 加載線程的鎖對(duì)象
lock = threading.Lock()
with lock :
  #...代碼

示例代碼:

import threading
from time import sleep, ctime
lock = threading.Lock()
def a():
  lock.acquire()
  for x in range(5):
    print(f'a:{str(x)}')
    sleep(0.01)
  lock.release()
def b():
  lock.acquire()
  for x in range(5):
    print(f'a:{str(x)}')
    sleep(0.01)
  lock.release()
threading.Thread(target=a).start()
threading.Thread(target=b).start()

相關(guān)屬性和方法

  • Thread對(duì)象的屬性

屬性描述
name線程名
ident線程的標(biāo)識(shí)符
daemon布爾標(biāo)志,表示這個(gè)線程是否是守護(hù)線程
  • Thread對(duì)象的方法

方法描述
init(group=None, tatget=None, name=None, args=(), kwargs ={}, verbose=None, daemon=None)實(shí)例化一個(gè)線程對(duì)象,需要有一個(gè)可調(diào)用的 target,以及其參數(shù) args 或 kwargs。還可以傳遞 name 或 group 參數(shù),不過(guò)后者還未實(shí)現(xiàn)。此 外 , verbose 標(biāo) 志 也 是 可 接 受 的 。 而 daemon 的 值 將 會(huì) 設(shè) 定 thread.daemon 屬性/標(biāo)志
start()開(kāi)始執(zhí)行該線程
run()定義線程功能的方法(通常在子類中被應(yīng)用開(kāi)發(fā)者重寫(xiě))
join (timeout=None)直至啟動(dòng)的線程終止之前一直掛起;除非給出了 timeout(秒),否則 會(huì)一直阻塞
is_alive()布爾標(biāo)志,表示這個(gè)線程是否還存活
  • threading模塊其他函數(shù)

函數(shù)描述
start()開(kāi)始執(zhí)行該線程
active_count()當(dāng)前活動(dòng)的 Thread 對(duì)象個(gè)數(shù)
enumerate()返回當(dāng)前活動(dòng)的 Thread 對(duì)象列表
settrace(func)為所有線程設(shè)置一個(gè) trace 函數(shù)
setprofile (func)為所有線程設(shè)置一個(gè) profile 函數(shù)
stack_size(size=0)返回新創(chuàng)建線程的棧大??;或?yàn)楹罄m(xù)創(chuàng)建的線程設(shè)定棧的大小 為 size
Lock()加載線程的鎖對(duì)象,是一個(gè)基本的鎖對(duì)象,一次只能一個(gè)鎖定,其余鎖請(qǐng)求,需等待鎖釋放后才能獲取,對(duì)象有acquire()和release()方法
RLock()多重鎖,在同一線程中可用被多次acquire。如果使用RLock,那么acquire和release必須成對(duì)出現(xiàn),調(diào)用了n次acquire鎖請(qǐng)求,則必須調(diào)用n次的release才能在線程中釋放鎖對(duì)象

后記

在Python多線程下,每個(gè)線程的執(zhí)行方式:

1、獲取GIL
2、執(zhí)行代碼直到sleep或者是python虛擬機(jī)將其掛起。
3、釋放GIL

通常來(lái)說(shuō),多線程是一個(gè)好東西。不過(guò)由于 Python 的 GIL 的限制,多線程更適合于 I/O 密集型應(yīng)用(I/O 釋放了 GIL,可以允 許更多的并發(fā)),而不是計(jì)算密集型應(yīng)用。對(duì)于后一種情況而言,為了實(shí)現(xiàn)更好的并行性,你需要使用多進(jìn)程,以便讓 CPU 的其他內(nèi)核來(lái)執(zhí)行。

請(qǐng)注意:多核多線程比單核多線程更差,原因是單核下多線程,每次釋放GIL,喚醒的那個(gè)線程都能獲取到GIL鎖,所以能夠無(wú)縫執(zhí)行,但多核下,CPU0釋放GIL后,其他CPU上的線程都會(huì)進(jìn)行競(jìng)爭(zhēng),但GIL可能會(huì)馬上又被CPU0拿到,導(dǎo)致其他幾個(gè)CPU上被喚醒后的線程會(huì)醒著等待到切換時(shí)間后又進(jìn)入待調(diào)度狀態(tài),這樣會(huì)造成線程顛簸(thrashing),導(dǎo)致效率更低

看完上述內(nèi)容,你們對(duì)怎么在python中實(shí)現(xiàn)多線程有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI