Python多線程編程之threading模塊詳解
一、介紹
線程是什么?線程有啥用?線程和進(jìn)程的區(qū)別是什么?
線程是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位。被包含在進(jìn)程中,是進(jìn)程中的實(shí)際運(yùn)作單位。一條線程指的是進(jìn)程中一個(gè)單一順序的控制流,一個(gè)進(jìn)程中可以并發(fā)多個(gè)線程,每條線程并行執(zhí)行不同的任務(wù)。
二、Python如何創(chuàng)建線程
2.1 方法一:
創(chuàng)建Thread對象
步驟:
1.目標(biāo)函數(shù)
2.實(shí)例化Thread對象
3.調(diào)用start()方法
import threading
# 目標(biāo)函數(shù)1
def fun1(num):
for i in range(num):
print('線程1: 第%d次循環(huán):' % i)
# 目標(biāo)函數(shù)2
def fun2(lst):
for ele in lst:
print('線程2: lst列表中元素 %d' % ele)
def main():
num = 10
# 實(shí)例化Thread對象
# target參數(shù)一定為一個(gè)函數(shù),且不帶括號
# args參數(shù)為元組類型,參數(shù)為一個(gè)時(shí)一定要加逗號
t1 = threading.Thread(target=fun1, args=(num,))
t2 = threading.Thread(target=fun2, args=([1, 2, 3, 4, 5],))
# 調(diào)用start方法
t1.start()
t2.start()
if __name__ == '__main__':
main()
2.2 方法二:
創(chuàng)建子類繼承threading.Thread類
import threading
import os
class Person(threading.Thread):
def run(self):
self.sing(5)
self.cook()
@staticmethod
def sing(num):
for i in range(num):
print('線程[%d]: The person sing %d song.' % (os.getpid(), i))
@staticmethod
def cook():
print('線程[%d]:The person has cooked breakfast.' % os.getpid())
def main():
p1 = Person()
p1.start()
p2 = Person()
p2.start()
if __name__ == '__main__':
main()
三、線程的用法
3.1 確定當(dāng)前的線程
import threading
import time
import logging
def fun1():
print(threading.current_thread().getName(), 'starting')
time.sleep(0.2)
print(threading.current_thread().getName(), 'exiting')
def fun2():
# print(threading.current_thread().getName(), 'starting')
# time.sleep(0.3)
# print(threading.current_thread().getName(), 'exiting')
logging.debug('starting')
time.sleep(0.3)
logging.debug('exiting')
logging.basicConfig(
level=logging.DEBUG,
format='[%(levelname)s] (%(threadName)-10s) %(message)s'
)
def main():
t1 = threading.Thread(name='線程1', target=fun1)
t2 = threading.Thread(name='線程2', target=fun2)
t1.start()
t2.start()
if __name__ == '__main__':
main()
3.2 守護(hù)線程
區(qū)別
- 普通線程:主線程等待子線程關(guān)閉后關(guān)閉
- 守護(hù)線程:管你子線程關(guān)沒關(guān),主線程到時(shí)間就關(guān)閉
守護(hù)線程如何搞
- 方法1:構(gòu)造線程時(shí)傳入dameon=True
- 方法2:調(diào)用setDaemon()方法并提供參數(shù)True
import threading
import time
import logging
def daemon():
logging.debug('starting')
# 添加延時(shí),此時(shí)主線程已經(jīng)退出,exiting不會(huì)打印
time.sleep(0.2)
logging.debug('exiting')
def non_daemon():
logging.debug('starting')
logging.debug('exiting')
logging.basicConfig(
level=logging.DEBUG,
format='[%(levelname)s] (%(threadName)-10s) %(message)s'
)
def main():
# t1 = threading.Thread(name='線程1', target=daemon)
# t1.setDaemon(True)
t1 = threading.Thread(name='線程1', target=daemon, daemon=True)
t2 = threading.Thread(name='線程2', target=non_daemon)
t1.start()
t2.start()
# 等待守護(hù)線程完成工作需要調(diào)用join()方法,默認(rèn)情況join會(huì)無限阻塞,可以傳入浮點(diǎn)值,表示超時(shí)時(shí)間
t1.join(0.2)
t2.join(0.1)
if __name__ == '__main__':
main()
3.3 控制資源訪問
目的:
Python線程中資源共享,如果不對資源加上互斥鎖,有可能導(dǎo)致數(shù)據(jù)不準(zhǔn)確。
import threading
import time
g_num = 0
def fun1(num):
global g_num
for i in range(num):
g_num += 1
print('線程1 g_num = %d' % g_num)
def fun2(num):
global g_num
for i in range(num):
g_num += 1
print('線程2 g_num = %d' % g_num)
def main():
t1 = threading.Thread(target=fun1, args=(1000000,))
t2 = threading.Thread(target=fun1, args=(1000000,))
t1.start()
t2.start()
if __name__ == '__main__':
main()
time.sleep(1)
print('主線程 g_num = %d' % g_num)
互斥鎖
import threading
import time
g_num = 0
L = threading.Lock()
def fun1(num):
global g_num
L.acquire()
for i in range(num):
g_num += 1
L.release()
print('線程1 g_num = %d' % g_num)
def fun2(num):
global g_num
L.acquire()
for i in range(num):
g_num += 1
L.release()
print('線程2 g_num = %d' % g_num)
def main():
t1 = threading.Thread(target=fun1, args=(1000000,))
t2 = threading.Thread(target=fun1, args=(1000000,))
t1.start()
t2.start()
if __name__ == '__main__':
main()
time.sleep(1)
print('主線程 g_num = %d' % g_num)
互斥鎖引發(fā)的另一個(gè)問題:死鎖
死鎖產(chǎn)生的原理:

import threading
import time
g_num = 0
L1 = threading.Lock()
L2 = threading.Lock()
def fun1():
L1.acquire(timeout=5)
time.sleep(1)
L2.acquire()
print('產(chǎn)生死鎖,并不會(huì)打印信息')
L2.release()
L1.release()
def fun2():
L2.acquire(timeout=5)
time.sleep(1)
L1.acquire()
print('產(chǎn)生死鎖,并不會(huì)打印信息')
L1.release()
L2.release()
def main():
t1 = threading.Thread(target=fun1)
t2 = threading.Thread(target=fun2)
t1.start()
t2.start()
if __name__ == '__main__':
main()
time.sleep(1)
print('主線程 g_num = %d' % g_num)
如何避免產(chǎn)生死鎖:
鎖超時(shí)操作
import threading
import time
g_num = 0
L1 = threading.Lock()
L2 = threading.Lock()
def fun1():
L1.acquire()
time.sleep(1)
L2.acquire(timeout=5)
print('超時(shí)異常打印信息1')
L2.release()
L1.release()
def fun2():
L2.acquire()
time.sleep(1)
L1.acquire(timeout=5)
print('超時(shí)異常打印信息2')
L1.release()
L2.release()
def main():
t1 = threading.Thread(target=fun1)
t2 = threading.Thread(target=fun2)
t1.start()
t2.start()
if __name__ == '__main__':
main()
time.sleep(1)
print('主線程 g_num = %d' % g_num)
到此這篇關(guān)于Python多線程編程之threading模塊詳解的文章就介紹到這了,更多相關(guān)python threading模塊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python3字符串的常用操作方法之修改方法與大小寫字母轉(zhuǎn)化
這篇文章主要介紹了Python3字符串的常用操作方法之修改方法與大小寫字母轉(zhuǎn)化,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
手把手教你使用Python創(chuàng)建微信機(jī)器人
微信,一個(gè)日活10億的超級app,不僅在國內(nèi)社交獨(dú)領(lǐng)風(fēng)騷,在國外社交也同樣占有一席之地,今天我們要將便是如何用Python來生成一個(gè)微信機(jī)器人,感興趣的朋友跟隨小編一起看看吧2019-04-04
pytorch之inception_v3的實(shí)現(xiàn)案例
今天小編就為大家分享一篇pytorch之inception_v3的實(shí)現(xiàn)案例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-01-01
通過shell+python實(shí)現(xiàn)企業(yè)微信預(yù)警
這篇文章主要介紹了通過shell+python實(shí)現(xiàn)企業(yè)微信預(yù)警,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03

