Python多線程學習(上)

最近在學習python多線程,寫一下隨筆就當複習了。另外強烈推薦你們看一下《Python核心編程》這本書,這本書裏面能夠幫你學習python進階。python

一。基本概念:編程

1.線程:多線程

線程又稱爲輕量級進程,線程之間能夠進行信息共享,線程能夠當作是主進程或‘主線程’的迷你進程。併發

2.進程:app

進程又稱爲重量級進程,進程之間是獨立的,進程間共享信息要經過  ‘進程間通訊(IPC)’  來進行。異步

3.同步:函數

同步是指一個任務執行完成後,另外一個任務接着進行,就是一個挨着一個,好比你在食堂打飯,一號窗口的菜好吃,有不少人排隊,只有前一個打完飯,後一個才能夠打飯。學習

4.異步:spa

異步是指多個任務同時進行,好比:食堂有不少打飯窗口,不少人能夠同時進行打飯。線程

5. I/O:

I/O是 input/output 的縮寫,表明的含義是讀寫。

6. I/O密集型代碼(I/O bound):

I/O密集型是指程序進行讀寫操做要不少,計算相對較少。

7. 計算密集型代碼(CPU bound):

計算密集型是指程序主要進行計算,而讀寫操做要少。

8. 併發和並行:

併發  CPU數量<線程數,併發是指相同時間間隔內執行任務。

並行 CPU數量>線程數,並行是指多個任務同時執行。

 

二。python和線程:

python代碼的執行由python虛擬機(解釋器主循環)來進行控制。

Python還有一個GIL(全局解釋器鎖),用來控制程序只有一個線程在執行。

GIL鎖執行過程:

鎖定

執行

釋放

返回第一步。

python執行多線程時,實際上是在相同時間間隔內對任務進行執行,每一個任務執行一小段時間,而後掛起,在執行下一個,一直到最後一個,而後在從第一個開始,如此往復,直至結束。

python對於I/O密集型比計算密集型要友好。緣由:python在執行I/O密集型程序時,主要進行文件讀寫,而計算較少,每一個任務在計算的同時能夠進行對其餘以完成的計算讀寫,而計算密集型對於計算較多,一直佔用CPU,致使處理效率不高,因此對於計算密集型並不友好。

三。threading模塊

對於多線程有thread模塊和threading模塊。thread模塊在執行多線程時,在不加鎖的狀況下,若是主線程結束,而子線程還未結束時,子線程會被強制中止,形成得不到運行結果。在進行多線程學習時推薦使用threading模塊。

1.添加線程:  threading.Thread(target=函數名,args=(須要傳入的參數))

Thread類還能夠傳入name,給線程命名。

2.判斷線程是否激活狀態: is_alive()

3.獲取線程數量:  threading.active_count()

4.獲取當前線程:threading.current_thread()

5.得到線程名字:.name

6.等待線程:。join()

等待與不等待:

等待:

 1 def a_1():
 2     print('aaaaa')
 3     time.sleep(3)
 4     print('aaaaa')
 5 def a_2():
 6     print('bbbbb')
 7     time.sleep(5)
 8     print('bbbbb')
 9 def main():
10     print('....start....')
11     t1 = threading.Thread(target=a_1)
12     t2 = threading.Thread(target=a_2)
13     t1.start()
14     t2.start()
15     print('....end....')
16 
17 if __name__ == '__main__':
18     main()
19 
20 
21 運行結果:
22 
23 ....start....
24 aaaaa
25 bbbbb
26 ....end....
27 aaaaa
28 bbbbb
29 
30 Process finished with exit code 0

從運行結果能夠看出,在不添加等待時,運行結果並非咱們想要的結果。

下面是不用 .join()的等待:

def main():
    print('....start....')
    t1 = threading.Thread(target=a_1)
    t2 = threading.Thread(target=a_2)
    t1.start()
    t2.start()
    time.sleep(8)
    print('....end....')

if __name__ == '__main__':
    main()

運行結果:
....start....
aaaaa
bbbbb
aaaaa
bbbbb
....end....

Process finished with exit code 0

經過添加time.sleep()語句使主線程等待子線程運行結束再運行下一步操做。打印內容均在...start....和...end...之間。

下面是運用  .join()進行等待。

def main():
    print('....start....')
    t1 = threading.Thread(target=a_1)
    t2 = threading.Thread(target=a_2)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print('....end....')

if __name__ == '__main__':
    main()

運行結果:
....start....
aaaaa
bbbbb
aaaaa
bbbbb
....end....

Process finished with exit code 0

經過該結果能夠看出,添加sleep()和join()效果同樣,可是推薦使用.join() ,由於在運行程序的時候用sleep()等待,還得知道等待時間,比較麻煩。join()較爲方便。

 

 

 

建立線程方法:

<1> 給Thread類傳入函數

<2>給Thread類傳入一個實例類

<3>建立一個子類,繼承Thread類,而後將函數傳入子類中

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

在不用多線程時:

import datetime
def num_1():
print('start...1')
time.sleep(2)
print('end...1')
def num_2():
print('start...2')
time.sleep(4)
print('end...2')

def main():
a = datetime.datetime.now()
print('.............start...........')
num_1()
num_2()
print('..............end............')
b = datetime.datetime.now()
p = (b - a).seconds
print('運行%s秒 ' % p)
if __name__ == '__main__':
main()

建立兩個函數,分別打印不一樣內容,並在程序執行完成打印執行時間。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

對於<1>

下面是示範代碼:   .join()是主線程等待子線程完成後在執行下一步操做。

 1 import threading
 2 import datetime
 3 #傳遞函數(1)
 4 def num(nu,se):
 5     print('%s  start' % nu)
 6     time.sleep(se)
 7     print('%s  end' % nu)
 8 
 9 time_list = [2,4]
10 def main():
11     a = datetime.datetime.now()
12     print('.....start.....')
13     thread_list = []
14     for x in range(len(time_list)):
15         t = threading.Thread(target=num,args=(x,time_list[x]))
16         thread_list.append(t)
17     for x in thread_list:
18         x.start()
19     for x in thread_list:
20         x.join()
21     print('.....end.....')
22     b = datetime.datetime.now()
23     p = (b - a).seconds
24     print('運行%s秒' % p)
25 if __name__ == '__main__':
26     main()

 

<2>添加一個實例類。

class New_1():
    def __init__(self,num,se):
        self.num = num
        self.se = se
    def __call__(self):
        return self.num(*self.se)
    pass

def main():
    a = datetime.datetime.now()
    print('...start...')
    thread_list = []
    for x in range(len(time_list)):
        t = threading.Thread(target=New_1(num,(x+1,time_list[x])))
        thread_list.append(t)
    for x in thread_list:
        x.start()
    for x in thread_list:
        x.join()
    print('...end...')
    b = datetime.datetime.now()
    p = (b-a).seconds
    print('運行%s秒' % p)
    
if __name__ == '__main__':
    main()
運行結果:

...start...
1 start
2 start
1 end
2 end
...end...
運行4秒

 
 

Process finished with exit code 0

3.繼承Thread類,將內容傳遞進子類中。

下面是代碼:

#傳遞函數(1)
def num(nu,se):
    print('%s  start' % nu)
    time.sleep(se)
    print('%s  end' % nu)
time_list = [2,4]
class New_2(threading.Thread):
    def __init__(self,num,se):
        super().__init__()
        self.num = num
        self.se = se
#對於下面的函數run()進行重寫,能夠靈活的定義函數,函數名必須是run(),不然在傳入參數後運行時,不會運行多線程。 def run(self): return self.num(*self.se) pass def main(): a = datetime.datetime.now() print('...start...') thread_list = [] for x in range(len(time_list)): t = New_2(num,(x,time_list[x])) thread_list.append(t) for x in thread_list: x.start() for x in thread_list: x.join() print('...end...') b = datetime.datetime.now() p = (b-a).seconds print('運行%s秒' % p) if __name__ == '__main__': main() 運行結果: ...start... 0 start 1 start 0 end 1 end ...end... 運行4秒 Process finished with exit code 0

對於以上三種添加線程的方法,能夠根據本身喜愛自行選擇,其餘的作了解便可。

 

若是有不對的地方,還請各位給予指正。

感謝你們的閱讀。

相關文章
相關標籤/搜索