Python3 并发编程4
Event事件
- 用来控制线程的执行
e.isSet()查看对象e当前的信号状态, 默认为Falsee.wait()信号状态为False, 则当前线程阻塞e.set()将e的信号状态设置为True, 被阻塞的线程进入非阻塞状态
from threading import Thread
from threading import Event
import time
e = Event()
def light():
print('*********红灯!**********')
print(f'对象当前的信号状态为{e.isSet()}')
time.sleep(5)
print('*********绿灯!**********')
e.set() # 将e的信号标准设为True
print(f'对象当前的信号状态为{e.isSet()}')
def driver(name):
print(f'{name}正在等红灯!')
e.wait() # 如果e信号标志为False, 则当前线程阻塞
print(f'{name}弹射起步!')
if __name__ == '__main__':
t1 = Thread(target=light)
t1.start()
for i in range(10):
t2 = Thread(target=driver, args=(f'老司机{i+1}号',))
t2.start()
'''
**********红灯!**********
对象当前的信号状态为False
老司机1号正在等红灯!
老司机2号正在等红灯!
老司机3号正在等红灯!
老司机4号正在等红灯!
老司机5号正在等红灯!
老司机6号正在等红灯!
老司机7号正在等红灯!
老司机8号正在等红灯!
老司机9号正在等红灯!
老司机10号正在等红灯!
**********绿灯!**********
对象当前的信号状态为True
老司机1号弹射起步!
老司机5号弹射起步!
老司机7号弹射起步!
老司机8号弹射起步!
老司机9号弹射起步!
老司机6号弹射起步!
老司机4号弹射起步!
老司机2号弹射起步!
老司机10号弹射起步!
老司机3号弹射起步!
'''
线程池与进程池
基本概念
- 用来控制当前程序允许创建进程/线程的数量
- 防止程序创建的进程/线程过多, 超过硬件承受的范围
使用方法
pool = ProcessPoolExecutor(5)当前任务最多只能同时开启5个进程, 默认线程个数是CPU个数pool = ThreadPoolExecutor(5)当前任务最多只能同时开启5个线程, 默认线程个数是CPU个数 * 5pool.submit(函数地址, 参数)提交任务pool.submit(函数地址, 参数).add_done_callback(回调函数地址)提交任务, 并把任务的返回值传给回调函数pool.shutdown()让线程池任务都执行完后再往下执行代码
from concurrent.futures import ThreadPoolExecutor
import time
pool = ThreadPoolExecutor(5)
# 送快递
def deliver(goods):
print(f'{goods}开始发货!')
time.sleep(1)
print(f'{goods}已经签收!')
return True
# 拿快递(回调函数)
def get_goods(res):
get = res.result()
if get:
print('开始拆快递!')
else:
print('投诉!')
for i in range(5):
pool.submit(deliver, f'格子衬衫牛仔裤{i+1}').add_done_callback(get_goods)
# 让线程池中的线程运行完毕再执行下面的代码
pool.shutdown()
print('钱包空空!')
'''
格子衬衫牛仔裤1开始发货!
格子衬衫牛仔裤2开始发货!
格子衬衫牛仔裤3开始发货!
格子衬衫牛仔裤4开始发货!
格子衬衫牛仔裤5开始发货!
格子衬衫牛仔裤1已经签收!
开始拆快递!
格子衬衫牛仔裤2已经签收!
开始拆快递!
格子衬衫牛仔裤5已经签收!
开始拆快递!
格子衬衫牛仔裤4已经签收!
开始拆快递!
格子衬衫牛仔裤3已经签收!
开始拆快递!
钱包空空!
'''
当我们想开启10个线程时, 既for i in range(10):, 结果如下
'''
格子衬衫牛仔裤1开始发货!
格子衬衫牛仔裤2开始发货!
格子衬衫牛仔裤3开始发货!
格子衬衫牛仔裤4开始发货!
格子衬衫牛仔裤5开始发货!
格子衬衫牛仔裤2已经签收!
开始拆快递!
格子衬衫牛仔裤6开始发货!
格子衬衫牛仔裤1已经签收!
开始拆快递!
格子衬衫牛仔裤7开始发货!
格子衬衫牛仔裤4已经签收!
格子衬衫牛仔裤3已经签收!
开始拆快递!
格子衬衫牛仔裤8开始发货!
格子衬衫牛仔裤5已经签收!
开始拆快递!
格子衬衫牛仔裤9开始发货!
开始拆快递!
格子衬衫牛仔裤10开始发货!
格子衬衫牛仔裤6已经签收!
开始拆快递!
格子衬衫牛仔裤10已经签收!
开始拆快递!
格子衬衫牛仔裤7已经签收!
开始拆快递!
格子衬衫牛仔裤8已经签收!
开始拆快递!
格子衬衫牛仔裤9已经签收!
开始拆快递!
钱包空空!
'''
和信号量的区别
- 信号量: 工作线程是我们自己创建的, 需要我们手动进行限流
- 线程池: 工作线程是线程池创建的, 线程池自动限流
协程(coroutine)
基本概念
- 在单线程下实行并发(切换 + 保存)
- 线程是系统级别的, 由操作系统调度. 协程是程序级别的, 需要程序员自己调度
- 优点: 不需要上下文切换的开销, 节省空间和时间
- 缺点: 无法利用多核优势, 进行阻塞操作会阻塞整个程序
实现方式
- yield实现
import time
# 生成器
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print(f'[CONSUMER] consuming {n}...')
time.sleep(1)
r = '200 OK'
def producer(c):
c.__next__() # 初始化生成器
n = 0
while n < 5:
n = n + 1
print(f'[PRODUCER] producing {n}...')
r = c.send(n) # 切换到consumer执行
print(f'[PRODUCER] consumer return: {r}')
c.close()
if __name__ == '__main__':
# c是生成器对象
c = consumer()
producer(c)
'''
[PRODUCER] producing 1...
[CONSUMER] consuming 1...
[PRODUCER] consumer return: 200 OK
[PRODUCER] producing 2...
[CONSUMER] consuming 2...
[PRODUCER] consumer return: 200 OK
[PRODUCER] producing 3...
[CONSUMER] consuming 3...
[PRODUCER] consumer return: 200 OK
[PRODUCER] producing 4...
[CONSUMER] consuming 4...
[PRODUCER] consumer return: 200 OK
[PRODUCER] producing 5...
[CONSUMER] consuming 5...
[PRODUCER] consumer return: 200 OK
'''
- gevent模块实现
from gevent import monkey;
monkey.patch_all() # 猴子补丁, 修改Python一些标准库
from gevent import spawn, joinall
import time
def func1():
print('1')
time.sleep(1)
def func2():
print('2')
time.sleep(2)
def func3():
print('3')
time.sleep(3)
start_time = time.time()
s1 = spawn(func1)
s2 = spawn(func2)
s3 = spawn(func3)
joinall([s1, s2, s3])
end_time = time.time()
print(end_time - start_time)
'''
1
2
3
3.007172107696533
'''
多线程爬取梨视频
from threading import Thread
import requests
import re
# 访问链接
def access_page(url):
response = requests.get(url)
return response
# 获取主页视频的id列表, 用来拼接视频详情页链接
def get_video_id(homepage_data):
id_list = re.findall('<a href="video_(.*?)" .*?>', homepage_data, re.S)
return id_list
# 获取视频链接列表
def get_video_url(detail_page_data):
video_url = re.findall('srcUrl="(.*?)"', detail_page_data, re.S)[0]
return video_url
# 获取视频名称
def get_video_name(detail_page_date):
video_name = re.findall('<h1 class="video-tt">(.*?)</h1>', detail_page_date, re.S)[0]
return video_name
# 保存视频
def save(video_data, name):
with open(f'{name}.mp4', 'wb') as f:
f.write(video_data)
print(f'视频[{name}]下载成功!')
def run(id):
# 拼接详情页链接并访问
detail_page_url = 'https://www.pearvideo.com/video_' + id
detail_page_data = access_page(detail_page_url).text
# 获取视频名称和视频链接
video_name = get_video_name(detail_page_data)
video_url = get_video_url(detail_page_data)
# 访问视频链接获取视频数据
video_data = access_page(video_url).content
# 保存视频数据
save(video_data, video_name)
if __name__ == '__main__':
homepage_data = access_page('https://www.pearvideo.com/').text
id_list = get_video_id(homepage_data)
# 多线程爬取
for id in id_list:
t = Thread(target=run, args=(id,))
t.start()
Python3 并发编程4的更多相关文章
- Python3 并发编程3
目录 GIL全局解释器锁 基本概念 多线程的作用 死锁现象 递归锁 信号量 线程队列 GIL全局解释器锁 基本概念 global interpreter lock 全局解释器锁 GIL不是Python ...
- Python3 并发编程小练习
实现基于TCP协议套接字,服务端实现接收客户端的连接并发 # server.py import socket from threading import Thread server = socket. ...
- Python3 并发编程2
目录 进程互斥锁 基本概念 互斥锁的使用 IPC 基本概念 队列 生产者消费者模型 基本概念 代码实现 线程 基本概念 创建线程 线程互斥锁 进程互斥锁 基本概念 临界资源: 一次仅允许一个进程使用的 ...
- Python3 并发编程1
目录 操作系统发展 穿孔卡片 批处理 多道技术(单核) 并发与并行 进程 程序与进程 进程调度 进程的三个状态 同步和异步 阻塞与非阻塞 僵尸进程与孤儿进程 守护进程 Python中的进程操作 Pro ...
- Python3 与 C# 并发编程之~ 进程篇
上次说了很多Linux下进程相关知识,这边不再复述,下面来说说Python的并发编程,如有错误欢迎提出- 如果遇到听不懂的可以看上一次的文章:https://www.cnblogs.com/dot ...
- Python3 与 C# 并发编程之~ 协程篇
3.协程篇¶ 去年微信公众号就陆陆续续发布了,我一直以为博客也汇总同步了,这几天有朋友说一直没找到,遂发现,的确是漏了,所以补上一篇 在线预览:https://github.lesschina.c ...
- Python3 与 C# 并发编程之~进程先导篇
在线预览:http://github.lesschina.com/python/base/concurrency/1.并发编程-进程先导篇.html Python3 与 C# 并发编程之- 进程篇 ...
- Python3 与 C# 并发编程之~ 线程篇
2.线程篇¶ 在线预览:https://github.lesschina.com/python/base/concurrency/3.并发编程-线程篇.html 示例代码:https://gith ...
- asyncio:python3未来并发编程主流、充满野心的模块
介绍 asyncio是Python在3.5中正式引入的标准库,这是Python未来的并发编程的主流,非常重要的一个模块.有一个web框架叫sanic,就是基于asyncio,语法和flask类似,使用 ...
随机推荐
- pat 1100 Mars Numbers(20 分)
1100 Mars Numbers(20 分) People on Mars count their numbers with base 13: Zero on Earth is called &qu ...
- lqb 基础练习 查找整数 (遍历)
基础练习 查找整数 时间限制:1.0s 内存限制:256.0MB 问题描述 给出一个包含n个整数的数列,问整数a在数列中的第一次出现是第几个. 输入格式 第一行包含一个整数n. 第二行包含 ...
- python 抓取youtube教程
前言: 相信大家很多人都看过youtube网站上的视频,网站上有很多的优质视频,清晰度也非常的高,看到喜欢的想要下载到本地,虽然也有很多方法,但是肯定没有python 来的快, 废话不多说,上代码: ...
- Redis单节点数据同步到Redis集群
一:Redis集群环境准备 1:需要先安装好Redis集群环境并配置好集群 192.168.0.113 7001-7003 192.168.0.162 7004-7006 2:检查redis集群 [r ...
- FPGA基础(verilog语言)——语法篇
verilog语言简介 verilog语言是一种语法类似于c的语言,但是与c语言也有不同之处,比如: 1.verilog语言是并行的,每个always块都是同时执行,而c语言是顺序执行的 2.veri ...
- iOS 抽奖轮盘效果实现思路
临近活动,相信不少app都会加一个新的需求——抽奖不多废话,先上GIF效果图 作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要这是一个我的iOS交流群:937194184,不管你是小白还是大牛欢 ...
- linux常规网卡配置正确,但是出不了路由的解决方法
netstat -rn #查看是网关 route add default gw 192.168.128.2 dev eth0 # 手动加入网关地址 此类情况容易出现在双网卡配置后
- Linux(CentOS7)修改默认yum源为国内的阿里云、网易yum源
修改方式: echo 备份当前的yum源 mv /etc/yum.repos.d /etc/yum.repos.d.backup4comex echo 新建空的yum源设置目录 mkdir /etc/ ...
- SpringAOP之使用切入点创建通知
之前已经说过了SpringAOP中的几种通知类型以及如何创建简单的通知见地址 一.什么是切入点 通过之前的例子中,我们可以创建ProxyFactory的方式来创建通知,然后获取目标类中的方法.通过不同 ...
- Selenium+Java(九)Selenium键盘与鼠标事件
一.键盘事件 ctrl+a driver.findElement(By.id("kw")).sendKeys(Keys.CONTROL, "a"); ctrl+ ...