[TOC]
<br>
### 線程的形象描述
在計算機運算中,我們會接觸到`進程`與`線程`兩個概念。
**進程:**如果我們同時打開QQ和微信,他們是在不同的進程中運行,他們數據不會共享。
**線程:**我們在播放視頻時,播放器一邊在播放頁面,一邊在播放聲音,他們是同時并且同步進行的,他們數據共享,這就是在同一個進程中的兩個線程。
### 單線程與多線程實例感受
假設:loop0() 執行需要4s,loop1()執行需要2s。
- 在`單線程`中,loop0與loop1`串行執行`,先完成loop0,再執行loop1.總共花費了4s+2s=6s
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from time import sleep
import time
def loop0():
print("loop 0 start...")
sleep(4) # 模擬休息4s
print("loop 0 done!")
def loop1():
print("loop 1 start...")
sleep(2) # 模擬休息2s
print("loop 1 done!")
if __name__ == '__main__':
start=time.time()
# 單線程,先執行loop0,loop0執行結束后再執行loop1
loop0()
loop1()
print("total time:%s" % (time.time()-start))
```
運行結果如:
```cmd
loop 0 start...
loop 0 done!
loop 1 start...
loop 1 done!
total time:6.001265048980713
```
<br>
- 在`多線程`中,loop0與loop1會`并行執行`,總共花的時間為最長線程時間4s
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import threading
import time
def loop0():
print("loop 0 start...")
time.sleep(4) # 模擬休息4s
print("loop 0 done!")
def loop1():
print("loop 1 start...")
time.sleep(2) # 模擬休息2s
print("loop 1 done!")
if __name__ == '__main__':
start=time.time()
# 創建兩個線程
t1 = threading.Thread(target=loop0,args=())
t2 = threading.Thread(target=loop1,args=())
# 線程開始
t1.start()
t2.start()
# 線程等候
t1.join()
t2.join()
print("total time:%s" % (time.time()-start))
```
運行結果:
```cmd
loop 0 start...
loop 1 start...
loop 1 done!
loop 0 done!
total time:4.009899854660034
```
---
### 快速掌握線程的寫法
#### 使用threading模塊的Thread類創建線程
竟然有3種方式創建線程,我們能夠熟練掌握其中一種就好。。。
##### 方式一:創建一個Thread的實例,傳給它一個函數
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import threading
import time
def watching_tv(tv_name):
"""看電視"""
for i in range(5):
time.sleep(1)
print("watching tv [%s] part[%s]..." %(tv_name,i))
def listening_music(music_name):
"""聽音樂"""
for i in range(5):
time.sleep(1.5)
print("listening music [%s] part[%s]" %(music_name,i))
if __name__ == '__main__':
# 創建兩個線程,一邊看電視,一邊聽音樂
print("The main program start...")
# 創建一個Thread實例,傳給它一個函數
t1 = threading.Thread(target=watching_tv,args=("殺破狼2",))
t2 = threading.Thread(target=listening_music,args=("愛如潮水",))
# 開始線程的執行
t1.start()
t2.start()
# 程序掛起,直到該子線程結束再繼續執行主線程;
t1.join()
t2.join()
print("The main program done!")
```
##### 方式二:創建一個Thread的實例,傳給它一個可調用的類對象
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import threading
import time
class Relex(object):
def __init__(self,func,*args):
self.func = func
self.args = args
def __call__(self):
self.func(*self.args)
def watching_tv(tv_name):
"""看電視"""
for i in range(5):
time.sleep(1)
print("watching tv [%s] part[%s]..." %(tv_name,i))
def listening_music(music_name):
"""聽音樂"""
for i in range(5):
time.sleep(1.5)
print("listening music [%s] part[%s]" %(music_name,i))
if __name__ == '__main__':
print("The main program start...")
# 創建兩個可調用類對象
cal_obj_1 = Relex(watching_tv,"殺破狼2")
cal_obj_2 = Relex(listening_music,"愛如潮水")
# 創建一個Thread實例,傳給它一個可調用類對象
t1 = threading.Thread(target=cal_obj_1)
t2 = threading.Thread(target=cal_obj_2)
# 開始線程的執行
t1.start()
t2.start()
# 程序掛起,直到該子線程結束再繼續執行主線程;
t1.join()
t2.join()
print("The main program done!")
```
##### 方式三:創建threading.Thread 的子類,重寫\__init__()與run()方法(通用)
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import threading
import time
class Relex(threading.Thread):
def __init__(self,func,*args):
super(Relex,self).__init__()
self.func = func
self.args = args
def run(self):
self.func(*self.args)
def watching_tv(tv_name):
"""看電視"""
for i in range(5):
time.sleep(1)
print("watching tv [%s] part[%s]..." %(tv_name,i))
def listening_music(music_name):
"""聽音樂"""
for i in range(5):
time.sleep(1.5)
print("listening music [%s] part[%s]" %(music_name,i))
if __name__ == '__main__':
print("The main program start...")
# 創建兩個繼承了thread.Thread類,并且重寫了run方法的子類。
t1 = Relex(watching_tv,"殺破狼2")
t2 = Relex(listening_music,"愛如潮水")
# 開始線程的執行
t1.start()
t2.start()
# 程序掛起,直到該子線程結束再繼續執行主線程;
t1.join()
t2.join()
print("The main program done!")
```
以上三個實例,運行的結果都是一樣的并行運行,運行結果如下:
```cmd
The main program start...
watching tv [殺破狼2] part[0]...
listening music [愛如潮水] part[0]
watching tv [殺破狼2] part[1]...
listening music [愛如潮水] part[1]
watching tv [殺破狼2] part[2]...
watching tv [殺破狼2] part[3]...
listening music [愛如潮水] part[2]
watching tv [殺破狼2] part[4]...
listening music [愛如潮水] part[3]
listening music [愛如潮水] part[4]
The main program done!
```
---
#### 多線程通過消息隊列queue交換信息
大家有沒有聽說過生產者與消費者模型?舉個廚師與顧客的例子:廚房中有兩個廚師,不停的在生產包子,四個顧客在餐廳中不停的吃包子。包子一邊在生產,一邊在被消耗,這個就是生產者與消費者模型了。
在這一小節,我們通過多線程與消息隊列來模擬這樣一個場景。

queue是有順序的,FIFO(先進先出),我們可以在腦中想象成它就是一個管道,廚師在左邊塞包子,顧客從右邊拿包子。
用代碼來解釋:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import threading, time
import queue # 導入消息隊列模塊
import random # 導入隨機數模塊,是為了模擬生產者與消費者速度不一致的情形
q = queue.Queue() # 實例化一個對象
def Producer(name): # 生產者函數
# 假設一個廚師只生產5個包子
for i in range(1,6):
q.put("%s_%s"%(name,i)) # 將結果放入消息隊列中
print("生產者 %s 生產了一個包子[%s_%s],現在總包子數:%s" % (name,name,i,q.qsize()))
time.sleep(random.randrange(2)) # 隨機每生產一個包子的速度
def Consumer(name): # 消費者函數
# 只要還有包子,顧客就一直吃吃吃...
while True:
data = q.get() # 取用消息隊列中存放的結果
print("消費者 %s 消費了一個包子[%s],包子剩余:%s" % (name, data, q.qsize()))
time.sleep(random.randrange(1, 3)) # 隨機每吃一個包子的速度
if __name__ == '__main__':
# 創建兩個廚師生產包子的線程
p1 = threading.Thread(target=Producer, args=('Milton',))
p2 = threading.Thread(target=Producer, args=('Cherish',))
# 創建兩個顧客吃包子的線程
c1 = threading.Thread(target=Consumer, args=('張三',))
c2 = threading.Thread(target=Consumer, args=('李四',))
# 線程運行
p1.start()
p2.start()
c1.start()
c2.start()
```
運行結果,如:
```cmd
生產者 Milton 生產了一個包子[Milton_1],現在總包子數:1
生產者 Cherish 生產了一個包子[Cherish_1],現在總包子數:2
消費者 張三 消費了一個包子[Milton_1],包子剩余:1
消費者 李四 消費了一個包子[Cherish_1],包子剩余:0
生產者 Cherish 生產了一個包子[Cherish_2],現在總包子數:1
生產者 Milton 生產了一個包子[Milton_2],現在總包子數:2
生產者 Milton 生產了一個包子[Milton_3],現在總包子數:3
生產者 Milton 生產了一個包子[Milton_4],現在總包子數:4
生產者 Milton 生產了一個包子[Milton_5],現在總包子數:5
消費者 張三 消費了一個包子[Cherish_2],包子剩余:4
消費者 李四 消費了一個包子[Milton_2],包子剩余:3
生產者 Cherish 生產了一個包子[Cherish_3],現在總包子數:4
生產者 Cherish 生產了一個包子[Cherish_4],現在總包子數:5
消費者 張三 消費了一個包子[Milton_3],包子剩余:4
消費者 李四 消費了一個包子[Milton_4],包子剩余:3
生產者 Cherish 生產了一個包子[Cherish_5],現在總包子數:4
消費者 李四 消費了一個包子[Milton_5],包子剩余:3
消費者 張三 消費了一個包子[Cherish_3],包子剩余:2
消費者 張三 消費了一個包子[Cherish_4],包子剩余:1
消費者 李四 消費了一個包子[Cherish_5],包子剩余:0
```
<hr style="margin-top:100px">
:-: 
***微信掃一掃,關注“python測試開發圈”,了解更多測試教程!***
- 前言
- chapter01_開發環境
- chapter02_字符串的使用
- chapter03_列表的使用
- chapter04_字典的使用
- chapter05_數字的使用
- chapter06_元組的使用
- chapter07_集合的使用
- chapter08_輸入輸出
- chapter09_控制流程
- chapter10_實例練習_登錄1
- chapter11_python函數入門
- chapter12_python中的類
- chapter13_輕松玩轉python中的模塊管理
- chapter14_掌握學習新模塊的技巧
- chapter15_通過os模塊與操作系統交互
- chapter16_子進程相關模塊(subprocess)
- chapter17_時間相關模塊(time & datetime)
- chapter18_序列化模塊(json)
- chapter19_加密模塊(hashlib)
- chapter20_文件的讀與寫
- chapter21_階段考核2_登錄
- chapter22_小小算法挑戰(排序&二分法)
- chapter23_用多線程來搞事!
- chapter24_HTTP接口請求(requests)
- chapter25_接口測試框架(pytest)
- chapter26_階段考核3_HTTP接口測試
- chapter27_HTML解析(pyquery)
- chapter28_階段考核4_爬蟲下載網易汽車
- chapter29_python中的那些編碼坑
- chapter30_MySQL數據庫操作
- chapter31 高級特性_迭代器與生成器
- chapter32 高級特性_裝飾器
- chapter33 高級特性_列表處理