1. 程式人生 > 實用技巧 >python的threading的使用(join方法,多執行緒,鎖threading.Lock和threading.Condition

python的threading的使用(join方法,多執行緒,鎖threading.Lock和threading.Condition

一、開啟多執行緒方法一

import threading,time

def write1():
    for i in range(1,5):
        print('1')
        time.sleep(1)


def write12():
    for i in range(1, 5):
        print('2')
        time.sleep(1)

# 給兩個函式開一個執行緒,target後面賦值函式名
t1 = threading.Thread(target=write1)
t2 = threading.Thread(target=write12)
# 使用start函式啟動這個執行緒 t1.start() t2.start() # 輸出執行緒數量 print(threading.enumerate()) ''' 輸出: 1 2 [<_MainThread(MainThread, started 21440)>, <Thread(Thread-1, started 2344)>, <Thread(Thread-2, started 3016)>] 1 2 2 1 2 1 '''

二、開啟多執行緒方法二

import threading,time

class Write1Threaq(threading.Thread):
    
def run(self): for i in range(1, 5): print('1----%s',threading.current_thread()) time.sleep(1) class Write2Threaq(threading.Thread): def run(self): for i in range(1, 5): # 輸出當前執行緒的名稱 print('2----%s',threading.current_thread()) time.sleep(
1) def main(): # 繼承自threading.Thread之後,只需要實現run方法,執行start函式後,會自動執行run函式 t1 = Write1Threaq() t2 = Write2Threaq() t1.start() t2.start() if __name__ == '__main__': main() ''' 輸出: 1----%s <Write1Threaq(Thread-1, started 6304)> 2----%s <Write2Threaq(Thread-2, started 10524)> 2----%s <Write2Threaq(Thread-2, started 10524)> 1----%s <Write1Threaq(Thread-1, started 6304)> 2----%s <Write2Threaq(Thread-2, started 10524)> 1----%s <Write1Threaq(Thread-1, started 6304)> 1----%s2----%s <Write2Threaq(Thread-2, started 10524)> <Write1Threaq(Thread-1, started 6304)> '''

三、不上鎖會出現的錯誤情況

import time,threading

value = 0
def write():
    global value
    for i in range(1,1000000):
        value+=1
    print(value)

def main():
    for i in range(1,3):
        t1 = threading.Thread(target=write)
        t1.start()

if __name__ == '__main__':
    main()

'''
輸出:
# 輸出結果顯然不符合我們的預期,例如value等於10的時候,兩個執行緒同時進行了兩次value+1,但是這個時候兩個value+1的
# value都是10,那麼結果value就是11,可以說少加了一次1
1143699
1227119
'''

四、使用Lock進行上鎖解決三的問題

import time,threading
# 使用多執行緒鎖
glock = threading.Lock()

value = 0
def write():
    global value
    # 上鎖
    glock.acquire()
    for i in range(1,1000000):
        value+=1
    # 解鎖
    glock.release()
    print(value)

def main():
    for i in range(1,3):
        t1 = threading.Thread(target=write)
        t1.start()

if __name__ == '__main__':
    main()

'''
輸出:這樣就沒問題了
999999
1999998
'''

五、condition的使用

threading.Condition 是一個繼承自threading.Lock的一個類,所以它也有上鎖解鎖的功能,上鎖acquire,解鎖ralease
同時它還具有wait()函式,其功能為將程式在此處阻塞,可以通過函式notify,或者notify_all來進行喚醒,這兩個函式要在release之前呼叫
notify執行一次只會喚醒一個執行緒,預設是第一個等待的執行緒
notify_all執行一次會喚醒所有的執行緒,

import random,threading,time

gcondition = threading.Condition()
Money = 0
def Create():
global Money
num=0
while 1:
if num>10:
break
gcondition.acquire()
Money += random.randint(1,100)
print('生產Money:',Money)
gcondition.notify_all()
gcondition.release()
num+=1
time.sleep(1)

def Consumer(money):
global Money
num = 0
while 1:
if num>2:
break
gcondition.acquire()
while Money<money:
print('金額不足!')
time.sleep(1)
gcondition.wait()
Money-=money
print('總錢數:%s,花費:%s,剩餘:%s。',Money+money,money,Money)
gcondition.release()
num+=1
time.sleep(1)


def main():
for i in range(1, 3):
t1 = threading.Thread(target=Create)
t1.start()
print('---------------------')
for i in range(1, 3):
ans = random.randint(1, 100)
t1 = threading.Thread(target=Consumer,args=[ans])# 注意這裡的引數傳遞方式
t1.start()




if __name__ == '__main__':
main()
'''
生產Money: 18
生產Money: 90
---------------------
總錢數:%s,花費:%s,剩餘:%s。 90 84 6
金額不足!
生產Money: 75
生產Money: 105
總錢數:%s,花費:%s,剩餘:%s。 105 84 21
金額不足!
生產Money: 107
生產Money: 171
總錢數:%s,花費:%s,剩餘:%s。 171 84 87
總錢數:%s,花費:%s,剩餘:%s。 87 26 61
生產Money: 146
總錢數:%s,花費:%s,剩餘:%s。 146 26 120
生產Money: 181
生產Money: 230
生產Money: 253
總錢數:%s,花費:%s,剩餘:%s。 253 26 227
生產Money: 275
生產Money: 320
生產Money: 350
生產Money: 423
生產Money: 431
生產Money: 484
生產Money: 527
生產Money: 596
生產Money: 646
生產Money: 721
生產Money: 788
生產Money: 850
'''

Thread引數傳遞的問題

錯誤的引數傳遞

threading.Thread(target=Consumer(ans))
import random,threading,time

gcondition = threading.Condition()
Money = 100
def Create():
    global Money
    num=0
    while 1:
        if num>10:
            break
        gcondition.acquire()
        Money += random.randint(1,100)
        print('生產Money:',Money)
        gcondition.notify_all()
        gcondition.release()
        num+=1
        time.sleep(1)

def Consumer(money):
    global Money
    num = 0
    while 1:
        if num>2:
            break
        gcondition.acquire()
        while Money<money:
            print('金額不足!')
            time.sleep(1)
            gcondition.wait()
        Money-=money
        print('總錢數:%s,花費:%s,剩餘:%s。',Money+money,money,Money)
        gcondition.release()
        num+=1
        time.sleep(1)


def main():
    for i in range(1, 3):
        print('222')
        ans = random.randint(1, 100)
        t1 = threading.Thread(target=Consumer(ans))
        t1.start()
    print('---------------------')

    for i in range(1, 3):
        t1 = threading.Thread(target=Create)
        t1.start()



if __name__ == '__main__':
    main()

'''
222
總錢數:%s,花費:%s,剩餘:%s。 100 12 88
總錢數:%s,花費:%s,剩餘:%s。 88 12 76
總錢數:%s,花費:%s,剩餘:%s。 76 12 64
222
總錢數:%s,花費:%s,剩餘:%s。 64 57 7
金額不足!

程式將會一直卡到這裡,這是因為你這樣的引數傳遞方式相當於在start函式沒有執行,函式就開始了執行
'''

正確的引數傳遞

threading.Thread(target=Consumer,args=[ans])

import random,threading,time

gcondition = threading.Condition()
Money = 100
def Create():
    global Money
    num=0
    while 1:
        if num>10:
            break
        gcondition.acquire()
        Money += random.randint(1,100)
        print('生產Money:',Money)
        gcondition.notify_all()
        gcondition.release()
        num+=1
        time.sleep(1)

def Consumer(money):
    global Money
    num = 0
    while 1:
        if num>2:
            break
        gcondition.acquire()
        while Money<money:
            print('金額不足!')
            time.sleep(1)
            gcondition.wait()
        Money-=money
        print('總錢數:%s,花費:%s,剩餘:%s。',Money+money,money,Money)
        gcondition.release()
        num+=1
        time.sleep(1)


def main():
    the_list = []
    for i in range(1, 3):
        ans = random.randint(1, 100)
        # 引數傳遞要這樣,可不能寫成Thread(target=Consumer(ans))
        t1 = threading.Thread(target=Consumer,args=[ans])
        #the_list.append(t1)
        t1.start()
    for i in the_list:
        i.start()
    print('---------------------')

    for i in range(1, 3):
        t1 = threading.Thread(target=Create)
        t1.start()



if __name__ == '__main__':
    main()

'''
總錢數:%s,花費:%s,剩餘:%s。 222
100 10 90
總錢數:%s,花費:%s,剩餘:%s。 90 17 ---------------------
73
生產Money: 99
生產Money: 141
生產Money: 162
總錢數:%s,花費:%s,剩餘:%s。 162 17 145
總錢數:%s,花費:%s,剩餘:%s。 145 10 135
生產Money: 164
生產Money: 228
總錢數:%s,花費:%s,剩餘:%s。 228 17 211
總錢數:%s,花費:%s,剩餘:%s。 211 10 201
生產Money: 203
生產Money: 249
生產Money: 276
生產Money: 321
生產Money: 342
生產Money: 387
生產Money: 475
生產Money: 501
生產Money: 596
生產Money: 682
生產Money: 731
生產Money: 823
生產Money: 888
生產Money: 975
生產Money: 1025
生產Money: 1056
生產Money: 1129
'''

六、join函式

如果一個執行緒在執行過程中要呼叫另外一個執行緒,並且等到其完成以後才能接著執行,解決方法就是“那麼在呼叫這個執行緒時可以使用被呼叫執行緒的join方法。”

下面先說一下setDeamon()吧:
其實主執行緒並不會結束setDeamon(True)的執行緒,而是當主執行緒執行完畢之後不會再去關注setDeamon(True)的執行緒。
所以setDeamon(True)的執行緒的結果是我們無法獲取到的,類似於愛咋咋地?不管你輸出什麼,我都不看,主執行緒跑
完就結束整個python process。
而setDeamon(False)的執行緒會一直受到主執行緒的關注,就算主執行緒跑完了也會等setDeamon(False)的執行緒跑完然後
再結束整個python process。
所以說,就算setDeamon(True)的執行緒在主執行緒之後跑完,但如果在setDeamon(False)的執行緒之前跑完的話,也是會
輸出結果的,而不是被所謂的主執行緒結束就殺死setDeamon(False)的執行緒。

看一下不同setDeamon執行結果:

#coding:utf-8
import threading
import time

def action(arg):
    for i in range(2):
        print('sub thread start!the thread name is:%s    ' % threading.currentThread().getName())
        print('the arg is:%s   ' %arg)
        time.sleep(1)

for i in range(4):
    t =threading.Thread(target=action,args=(i,))
    t.setDaemon(True)
    t.start()

print('main_thread end!')

'''
setDaemon(True)的輸出:
sub thread start!the thread name is:Thread-1    
the arg is:0   
sub thread start!the thread name is:Thread-2    
the arg is:1   
sub thread start!the thread name is:Thread-3    
the arg is:2   
sub thread start!the thread name is:Thread-4    
the arg is:3   
main_thread end!
'''
'''
setDaemon(False)的輸出:
sub thread start!the thread name is:Thread-1    
the arg is:0   
sub thread start!the thread name is:Thread-2    
the arg is:1   
sub thread start!the thread name is:Thread-3    
the arg is:2   
sub thread start!the thread name is:Thread-4    
the arg is:3   
main_thread end!
sub thread start!the thread name is:Thread-2    sub thread start!the thread name is:Thread-1    
the arg is:0   

the arg is:1   
sub thread start!the thread name is:Thread-3    
the arg is:2   
sub thread start!the thread name is:Thread-4    
the arg is:3   
'''

join使用的程式正確書寫方法:

#coding:utf-8
import threading
import time

def action(arg):
    time.sleep(1)
    print('sub thread start!the thread name is:%s    ' % threading.currentThread().getName())
    print('the arg is:%s   ' %arg)
    time.sleep(1)

#不正確寫法,會導致多執行緒順序執行,失去了多執行緒的意義
for i in range(4):
    t =threading.Thread(target=action,args=(i,))
    t.setDaemon(True)
    t.start()
    t.join()

#正確寫法
thread_list = []    #執行緒存放列表
for i in range(4):
    t =threading.Thread(target=action,args=(i,))
    t.setDaemon(True)
    thread_list.append(t)

for t in thread_list:
    t.start()

for t in thread_list:
    t.join()
print('main_thread end!')