python多線程懒构、鎖餐济、event事件機(jī)制的簡(jiǎn)單使用

線程和進(jìn)程

1、線程共享創(chuàng)建它的進(jìn)程的地址空間,進(jìn)程有自己的地址空間

2胆剧、線程可以訪問進(jìn)程所有的數(shù)據(jù)絮姆,線程可以相互訪問

3、線程之間的數(shù)據(jù)是獨(dú)立的

4秩霍、子進(jìn)程復(fù)制線程的數(shù)據(jù)

5篙悯、子進(jìn)程啟動(dòng)后是獨(dú)立的 ,父進(jìn)程只能殺掉子進(jìn)程铃绒,而不能進(jìn)行數(shù)據(jù)交換

6鸽照、修改線程中的數(shù)據(jù),都是會(huì)影響其他的線程颠悬,而對(duì)于進(jìn)程的更改矮燎,不會(huì)影響子進(jìn)程

threading.Thread

Thread 是threading模塊中最重要的類之一,可以使用它來創(chuàng)建線程赔癌。有兩種方式來創(chuàng)建線程:一種是通過繼承Thread類漏峰,重寫它的run方法;另一種是創(chuàng)建一個(gè)threading.Thread對(duì)象届榄,在它的初始化函數(shù)(init)中將可調(diào)用對(duì)象作為參數(shù)傳入浅乔。

先來看看通過繼承threading.Thread類來創(chuàng)建線程的例子:

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

另外一種創(chuàng)建線程的方法:

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() 設(shè)置線程名稱
Thread.name 線程名稱

Thread.ident 獲取線程的標(biāo)識(shí)符。線程標(biāo)識(shí)符是一個(gè)非零整數(shù),只有在調(diào)用了start()方法之后該屬性才有效靖苇,否則它只返回None

判斷線程是否是激活的(alive)席噩。從調(diào)用start()方法啟動(dòng)線程,到run()方法執(zhí)行完畢或遇到未處理異常而中斷 這段時(shí)間內(nèi)贤壁,線程是激活的

Thread.is_alive()
Thread.isAlive()

Thread.join([timeout]) 調(diào)用Thread.join將會(huì)使主調(diào)線程堵塞悼枢,直到被調(diào)用線程運(yùn)行結(jié)束或超時(shí)。參數(shù)timeout是一個(gè)數(shù)值類型脾拆,表示超時(shí)時(shí)間馒索,如果未提供該參數(shù),那么主調(diào)線程將一直堵塞到被調(diào)線程結(jié)束

Python GIL(Global Interpreter Lock)

GIL并不是Python的特性名船,它是在實(shí)現(xiàn)Python解析器(CPython)時(shí)所引入的一個(gè)概念绰上。就好比C++是一套語言(語法)標(biāo)準(zhǔn),但是可以用不同的編譯器來編譯成可執(zhí)行代碼渠驼。有名的編譯器例如GCC蜈块,INTEL C++,Visual C++等迷扇。Python也一樣百揭,同樣一段代碼可以通過CPython,PyPy蜓席,Psyco等不同的Python執(zhí)行環(huán)境來執(zhí)行器一。像其中的JPython就沒有GIL。然而因?yàn)镃Python是大部分環(huán)境下默認(rèn)的Python執(zhí)行環(huán)境厨内。所以在很多人的概念里CPython就是Python盹舞,也就想當(dāng)然的把GIL歸結(jié)為Python語言的缺陷。所以這里要先明確一點(diǎn):GIL并不是Python的特性隘庄,Python完全可以不依賴于GIL踢步。

線程鎖的使用:

# 鎖:GIL 全局解釋器 它是為了保證線程在運(yùn)行過程中不被搶占
number = 0
lock = threading.RLock()    # 創(chuàng)建鎖


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中,創(chuàng)建了子線程B丑掺,并且在主線程A中調(diào)用了B.setDaemon(),這個(gè)的意思是获印,把主線程A設(shè)置為守護(hù)線程,這時(shí)候街州,要是主線程A執(zhí)行結(jié)束了兼丰,就不管子線程B是否完成,一并和主線程A退出.這就是setDaemon方法的含義,這基本和join是相反的唆缴。此外鳍征,還有個(gè)要特別注意的:必須在start() 方法調(diào)用之前設(shè)置,如果不設(shè)置為守護(hù)線程面徽,程序會(huì)被無限掛起艳丛。

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)     # 設(shè)置子線程是否跟隨主線程一起結(jié)束
thread1.start()
time.sleep(1)
print('satrt join')
# thread1.join()    # 使主線程阻塞匣掸,直至子線程運(yùn)行完畢再繼續(xù)主線程
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)  # 將主線程設(shè)置為Daemon線程,它退出時(shí),其它子線程會(huì)同時(shí)退出,不管是否執(zhí)行完任務(wù)
m.start()
# m.join()    # 使主線程阻塞,直至子線程運(yùn)行完畢再繼續(xù)主線程
print("---main thread done----")

線程鎖(互斥鎖Mutex)
一個(gè)進(jìn)程下可以啟動(dòng)多個(gè)線程氮双,多個(gè)線程共享父進(jìn)程的內(nèi)存空間碰酝,也就意味著每個(gè)線程可以訪問同一份數(shù)據(jù),此時(shí)戴差,如果2個(gè)線程同時(shí)要修改同一份數(shù)據(jù)送爸,會(huì)出現(xiàn)什么狀況?

num = 100  # 設(shè)定一個(gè)共享變量

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

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

for t in thread_list: # 等待所有線程執(zhí)行完畢
    t.join()

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

def subNum():
    global num  # 在每個(gè)線程中都獲取這個(gè)全局變量
    print('--get num:', num)
    time.sleep(1)
    lock.acquire()  # 修改數(shù)據(jù)前加鎖
    num -= 1  # 對(duì)此公共變量進(jìn)行-1操作
    lock.release()  # 修改后釋放


num = 100  # 設(shè)定一個(gè)共享變量
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:  # 等待所有線程執(zhí)行完畢
    t.join()

print('final num:', num)

Rlock與Lock的區(qū)別:
RLock允許在同一線程中被多次acquire暖释。而Lock卻不允許這種情況袭厂。否則會(huì)出現(xiàn)死循環(huán),程序不知道解哪一把鎖球匕。注意:如果使用RLock纹磺,那么acquire和release必須成對(duì)出現(xiàn),即調(diào)用了n次acquire谐丢,必須調(diào)用n次的release才能真正釋放所占用的鎖
<br />
Events

Python提供了Event對(duì)象用于線程間通信,它是由線程設(shè)置的信號(hào)標(biāo)志蚓让,如果信號(hào)標(biāo)志位真乾忱,則其他線程等待直到信號(hào)接觸。

Event對(duì)象實(shí)現(xiàn)了簡(jiǎn)單的線程通信機(jī)制历极,它提供了設(shè)置信號(hào)窄瘟,清除信號(hào),等待等用于實(shí)現(xiàn)線程間的通信趟卸。

event = threading.Event() 創(chuàng)建一個(gè)event

1 設(shè)置信號(hào)
event.set()

使用Event的set()方法可以設(shè)置Event對(duì)象內(nèi)部的信號(hào)標(biāo)志為真蹄葱。Event對(duì)象提供了isSet()方法來判斷其內(nèi)部信號(hào)標(biāo)志的狀態(tài)。
當(dāng)使用event對(duì)象的set()方法后锄列,isSet()方法返回真

2 清除信號(hào)
event.clear()

使用Event對(duì)象的clear()方法可以清除Event對(duì)象內(nèi)部的信號(hào)標(biāo)志图云,即將其設(shè)為假,當(dāng)使用Event的clear方法后邻邮,isSet()方法返回假

3 等待
event.wait()

Event對(duì)象wait的方法只有在內(nèi)部信號(hào)為真的時(shí)候才會(huì)很快的執(zhí)行并完成返回竣况。當(dāng)Event對(duì)象的內(nèi)部信號(hào)標(biāo)志位假時(shí),
則wait方法一直等待到其為真時(shí)才返回筒严。也就是說必須set新號(hào)標(biāo)志位真

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('輸入內(nèi)容:')
if inp == 'true':
    event_obj.set()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末丹泉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鸭蛙,更是在濱河造成了極大的恐慌摹恨,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娶视,死亡現(xiàn)場(chǎng)離奇詭異晒哄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門揩晴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勋陪,“玉大人,你說我怎么就攤上這事硫兰∽缬蓿” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵劫映,是天一觀的道長(zhǎng)违孝。 經(jīng)常有香客問我,道長(zhǎng)泳赋,這世上最難降的妖魔是什么雌桑? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮祖今,結(jié)果婚禮上校坑,老公的妹妹穿的比我還像新娘。我一直安慰自己千诬,他們只是感情好耍目,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著徐绑,像睡著了一般邪驮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上傲茄,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天毅访,我揣著相機(jī)與錄音,去河邊找鬼盘榨。 笑死喻粹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的草巡。 我是一名探鬼主播磷斧,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼捷犹!你這毒婦竟也來了弛饭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤萍歉,失蹤者是張志新(化名)和其女友劉穎侣颂,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體枪孩,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡憔晒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年藻肄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拒担。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嘹屯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出从撼,到底是詐尸還是另有隱情州弟,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布低零,位于F島的核電站婆翔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏掏婶。R本人自食惡果不足惜啃奴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雄妥。 院中可真熱鬧最蕾,春花似錦、人聲如沸老厌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梅桩。三九已至壹粟,卻和暖如春拜隧,著一層夾襖步出監(jiān)牢的瞬間宿百,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工洪添, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留垦页,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓干奢,卻偏偏與公主長(zhǎng)得像痊焊,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子忿峻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 目錄 一、開啟線程的兩種方式 在python中開啟線程要導(dǎo)入threading觉壶,它與開啟進(jìn)程所需要導(dǎo)入的模塊mul...
    CaiGuangyin閱讀 2,403評(píng)論 1 16
  • 線程 引言&動(dòng)機(jī) 考慮一下這個(gè)場(chǎng)景脑题,我們有10000條數(shù)據(jù)需要處理,處理每條數(shù)據(jù)需要花費(fèi)1秒铜靶,但讀取數(shù)據(jù)只需要0....
    不浪漫的浪漫_ea03閱讀 362評(píng)論 0 0
  • 引言&動(dòng)機(jī) 考慮一下這個(gè)場(chǎng)景叔遂,我們有10000條數(shù)據(jù)需要處理,處理每條數(shù)據(jù)需要花費(fèi)1秒旷坦,但讀取數(shù)據(jù)只需要0.1秒掏熬,...
    chen_000閱讀 511評(píng)論 0 0
  • Num01-->多線程threading Python中建議使用threading模塊通贞,而不要使用thread模塊...
    曉可加油閱讀 1,021評(píng)論 2 7
  • 最近常愛吃附近小巷里一家沙茶面 本打算今晚記述 但因時(shí)間太晚 不妨明早起來再敘晚上作畫
    Kislyl閱讀 444評(píng)論 0 0