1. Python多線程
python3中常用的線程模塊為:_thread(Python2中的thread)袁梗、threading(推薦)
線程池:ThreadPoolExecutor
2. 使用線程
第一種方式:
_thread.start_new_thread(function,args[,kwargs])
function:線程函數(shù)
args:傳遞給線程函數(shù)的參數(shù)撬统,必須是tuple(元組)類型
kwargs:可選參數(shù)
第二種方式
import threading
import time
import traceback
class MyThread(threading.Thread):
def __init__(self, thread_id, thread_name, counter):
threading.Thread.__init__(self)
self.thread_id = thread_id
self.thread_name = thread_name
self.counter = counter
def run(self):
print_time(self.thread_name, 1, self.counter)
def print_time(thread_name, delay, counter):
while counter:
time.sleep(delay)
print('%s : %s ' % (thread_name, time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())))
counter -= 1
thread1 = MyThread(1, 'time_thread_1', 5)
thread1.start()
3. thread內(nèi)部方法
1.threading.currentThread():返回當前的線程變量两曼;
2.threading.enumerate():返回當前正在運行的線程list,正在運行是指線程啟動后、結(jié)束前斧散;
3.threading.activeCount():返回當前正在運行的線程數(shù)供常,與len(threading.enumerate())相同
4.run():用以表示線程互動的方法
5.start():啟動線程
6.join([time]):等待線程終止,阻塞線程,直至join被調(diào)用或者線程中止鸡捐、正常退出或者異常栈暇,或者超時[time]
7.isAlive():返回線程是否存活
8.getName():線程名
9.setName():設(shè)置線程名
4. 線程同步
如果多個線程同時修改某個數(shù)據(jù)可能會導(dǎo)致數(shù)據(jù)的正確性,所以需要對線程進行同步處理箍镜,使用 Thread 對象的 Lock 和 Rlock 可以實現(xiàn)簡單的線程同步源祈,這兩個對象都有 acquire 方法和 release 方法,對于那些需要每次只允許一個線程操作的數(shù)據(jù)色迂,可以將其操作放到 acquire 和 release 方法之間香缺。
如下實例,多個線程同時進行對某個數(shù)據(jù)進行自增脚草,未加鎖時結(jié)果會怎么樣赫悄?
import threading
class MyThread(threading.Thread):
def __init__(self, thread_id, thread_name):
threading.Thread.__init__(self)
self.thread_id = thread_id
self.thread_name = thread_name
def run(self):
#獲取鎖
# threadLock.acquire()
global number
for i in range(1, 1000000):
number += 1
#釋放鎖
# threadLock.release()
threadLock = threading.Lock()
number = 1
thread = MyThread(1, 'thread')
thread.start()
thread2 = MyThread(2, 'thread2')
thread2.start()
計算結(jié)果=1999999 ? 1313434
Process finished with exit code 0
如果把加鎖的注釋去掉會怎么樣呢?
def run(self):
#獲取鎖
threadLock.acquire()
global number
for i in range(1, 1000000):
number += 1
#釋放鎖
threadLock.release()
計算結(jié)果=1999999 ? 1999999
Process finished with exit code 0
5. 線程池
1. 線程池介紹
線程池在系統(tǒng)啟動時即創(chuàng)建大量空閑的線程馏慨,程序只要將一個函數(shù)提交給線程池埂淮,線程池就會啟動一個空閑的線程來執(zhí)行它。當該函數(shù)執(zhí)行結(jié)束后写隶,該線程并不會死亡倔撞,而是再次返回到線程池中變成空閑狀態(tài),等待執(zhí)行下一個函數(shù)慕趴。
使用線程池可以有效地控制系統(tǒng)中并發(fā)線程的數(shù)量痪蝇。當系統(tǒng)中包含有大量的并發(fā)線程時,會導(dǎo)致系統(tǒng)性能急劇下降冕房,甚至導(dǎo)致 Python 解釋器崩潰躏啰,而線程池的最大線程數(shù)參數(shù)可以控制系統(tǒng)中并發(fā)線程的數(shù)量不超過此數(shù)。
2.線程池的使用
線程池的基類是 concurrent.futures 模塊中的 Executor耙册,Executor 提供了兩個子類给僵,即 ThreadPoolExecutor 和 ProcessPoolExecutor,其中 ThreadPoolExecutor 用于創(chuàng)建線程池,而 ProcessPoolExecutor 用于創(chuàng)建進程池帝际。
Exectuor 提供了如下常用方法:
submit(fn, *args, **kwargs):將 fn 函數(shù)提交給線程池蔓同。*args 代表傳給 fn 函數(shù)的參數(shù),*kwargs 代表以關(guān)鍵字參數(shù)的形式為 fn 函數(shù)傳入?yún)?shù)蹲诀。
map(func, *iterables, timeout=None, chunksize=1):該函數(shù)類似于全局函數(shù) map(func, *iterables)斑粱,只是該函數(shù)將會啟動多個線程,以異步方式立即對 iterables 執(zhí)行 map 處理脯爪。
shutdown(wait=True):關(guān)閉線程池则北。
submit 方法會返回一個 Future 對象
Future 提供了如下方法:
cancel():取消該 Future 代表的線程任務(wù)。如果該任務(wù)正在執(zhí)行披粟,不可取消咒锻,則該方法返回 False;否則守屉,程序會取消該任務(wù)惑艇,并返回 True。
cancelled():返回 Future 代表的線程任務(wù)是否被成功取消拇泛。
running():如果該 Future 代表的線程任務(wù)正在執(zhí)行滨巴、不可被取消,該方法返回 True俺叭。
done():如果該 Funture 代表的線程任務(wù)被成功取消或執(zhí)行完成,則該方法返回 True熄守。
result(timeout=None):獲取該 Future 代表的線程任務(wù)最后返回的結(jié)果蜈垮。如果 Future 代表的線程任務(wù)還未完成,該方法將會阻塞當前線程裕照,其中 timeout 參數(shù)指定最多阻塞多少秒攒发。
exception(timeout=None):獲取該 Future 代表的線程任務(wù)所引發(fā)的異常。如果該任務(wù)成功完成晋南,沒有異常惠猿,則該方法返回 None。
add_done_callback(fn):為該 Future 代表的線程任務(wù)注冊一個“回調(diào)函數(shù)”负间,當該任務(wù)成功完成時偶妖,程序會自動觸發(fā)該 fn 函數(shù)。
在用完一個線程池后政溃,應(yīng)該調(diào)用該線程池的 shutdown() 方法趾访,該方法將啟動線程池的關(guān)閉序列。調(diào)用 shutdown() 方法后的線程池不再接收新任務(wù)董虱,但會將以前所有的已提交任務(wù)執(zhí)行完成腹缩。當線程池中的所有任務(wù)都執(zhí)行完成后,該線程池中的所有線程都會死亡空扎。
from concurrent.futures import ThreadPoolExecutor
import threading, time
class WorkThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self) -> None:
print('start run task')
time.sleep(2)
print('task finish\n')
return '線程類執(zhí)行完成'
def test(thread_name):
print(thread_name, threading.current_thread().name)
time.sleep(5)
print('任務(wù)完成\n')
return '線程方法執(zhí)行完成'
f __name__ == '__main__':
thread_pool = ThreadPoolExecutor(5)
futures = []
for i in range(7):
thraed = WorkThread()
# sumit(方法名藏鹊,參數(shù))
future1 = thread_pool.submit(thraed.run)
future2 = thread_pool.submit(test, i)
futures.append(future1)
futures.append(future2)
def get_call_back(future):
# 監(jiān)聽任務(wù)執(zhí)行結(jié)果,當前線程一直阻塞知道有結(jié)果转锈,但是不阻塞主線程
print(future.result())
for future in futures:
#添加監(jiān)聽
future.add_done_callback(get_call_back)
print('main thread')
map執(zhí)行線程
啟動三個線程
thread_pool.map(test, (2,3,4))