[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交换信息
大家有没有听说过生产者与消费者模型?举个厨师与顾客的例子:厨房中有两个厨师,不停的在生产包子,四个顾客在餐厅中不停的吃包子。包子一边在生产,一边在被消耗,这个就是生产者与消费者模型了。
在这一小节,我们通过多线程与消息队列来模拟这样一个场景。
![](https://box.kancloud.cn/7323a5966fef0e1deeab34367f1867ca_575x366.jpg)
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">
:-: ![](https://box.kancloud.cn/2ff0bc02ec938fef8b6dd7b7f16ee11d_258x258.jpg)
***微信扫一扫,关注“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 高级特性_列表处理