Event事件

  • 用来控制线程的执行
  • e.isSet()查看对象e当前的信号状态, 默认为False
  • e.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个数 * 5
  • pool.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的更多相关文章

  1. Python3 并发编程3

    目录 GIL全局解释器锁 基本概念 多线程的作用 死锁现象 递归锁 信号量 线程队列 GIL全局解释器锁 基本概念 global interpreter lock 全局解释器锁 GIL不是Python ...

  2. Python3 并发编程小练习

    实现基于TCP协议套接字,服务端实现接收客户端的连接并发 # server.py import socket from threading import Thread server = socket. ...

  3. Python3 并发编程2

    目录 进程互斥锁 基本概念 互斥锁的使用 IPC 基本概念 队列 生产者消费者模型 基本概念 代码实现 线程 基本概念 创建线程 线程互斥锁 进程互斥锁 基本概念 临界资源: 一次仅允许一个进程使用的 ...

  4. Python3 并发编程1

    目录 操作系统发展 穿孔卡片 批处理 多道技术(单核) 并发与并行 进程 程序与进程 进程调度 进程的三个状态 同步和异步 阻塞与非阻塞 僵尸进程与孤儿进程 守护进程 Python中的进程操作 Pro ...

  5. Python3 与 C# 并发编程之~ 进程篇

      上次说了很多Linux下进程相关知识,这边不再复述,下面来说说Python的并发编程,如有错误欢迎提出- 如果遇到听不懂的可以看上一次的文章:https://www.cnblogs.com/dot ...

  6. Python3 与 C# 并发编程之~ 协程篇

      3.协程篇¶ 去年微信公众号就陆陆续续发布了,我一直以为博客也汇总同步了,这几天有朋友说一直没找到,遂发现,的确是漏了,所以补上一篇 在线预览:https://github.lesschina.c ...

  7. Python3 与 C# 并发编程之~进程先导篇

      在线预览:http://github.lesschina.com/python/base/concurrency/1.并发编程-进程先导篇.html Python3 与 C# 并发编程之- 进程篇 ...

  8. Python3 与 C# 并发编程之~ 线程篇

      2.线程篇¶ 在线预览:https://github.lesschina.com/python/base/concurrency/3.并发编程-线程篇.html 示例代码:https://gith ...

  9. asyncio:python3未来并发编程主流、充满野心的模块

    介绍 asyncio是Python在3.5中正式引入的标准库,这是Python未来的并发编程的主流,非常重要的一个模块.有一个web框架叫sanic,就是基于asyncio,语法和flask类似,使用 ...

随机推荐

  1. 一、netcore跨平台之 Linux上部署netcore和webapi

    这几天闲着的时候在linux上部署了一下netcore webapi,下面就纪要一下这个过程. 中间遇到不少的坑,心里都是泪啊. 话不多说,开始干活. ------------------------ ...

  2. Idea集成SpringBoot实现两种热部署方式(亲测有效)

    即将介绍的两种热部署方式: 1.SpringLoaded 2.DevTools 区别: SpringLoader:SpringLoader 在部署项目时使用的是热部署的方式. DevTools:Dev ...

  3. windows anaconda python3.7 import ssl,psycopg2报错

    使用anaconda,本来是为了减少装第三方模块依赖出错问题的. 但是,今天发现,也是有坑啊. 首先 import ssl 报错,import _ssl 说DLL load failed 解决办法:用 ...

  4. python3 之 趣味数学题(爱因斯坦)

    爱因斯坦曾出过这样一道有趣的数学题: 有一个长阶梯,若每步上 2 阶,最 后剩 1 阶; 若每步上 3 阶,最后剩 2 阶; 若每步上 5 阶,最后剩 4 阶; 若每步上 6 阶,最后剩 5 阶; 只 ...

  5. 2019-9-10:渗透测试,基础学习,nmap扫描命令,php基本语法学习,笔记

    nmap参数-sT,使用tcp全连接的方式 ,扫描过程需要三次握手,建立链接,则说明端口开放,扫描速度慢-sS,使用syn的数据包去检测,接收到ACK说明端口开放-sN,null扫描,发出去的数据包不 ...

  6. (六)OpenStack---M版---双节点搭建---Neutron安装和配置

    ↓↓↓↓↓↓↓↓视频已上线B站↓↓↓↓↓↓↓↓ >>>>>>传送门 1.创建网络服务数据库 2.获得 admin 凭证来获取只有管理员能执行的命令的访问权限 3.创 ...

  7. 为什么 Redis 是单线程的?

    以前一直有个误区,以为:高性能服务器 一定是 多线程来实现的 原因很简单因为误区二导致的:多线程 一定比 单线程 效率高.其实不然. 在说这个事前希望大家都能对 CPU . 内存 . 硬盘的速度都有了 ...

  8. IEnumerable和IEnumerator详解

    引言 IEnumerable是可枚举的所有非泛型集合的基接口, IEnumerable包含一个方法GetEnumerator(),该方法返回一个IEnumerator:IEnumerator提供通过C ...

  9. MySQL 格式化时间 成字符串

    创建个表: CREATE TABLE `x02基本信息` ( `ID` varchar(32) NOT NULL COMMENT '系统内记录的唯一标识,供系统内部使用.', `名称` varchar ...

  10. jitter()函数的使用

    jitter()函数:对数值向量添加一个小的噪音量. jitter(x,factor=1,amount=NULL) ·x:数值变量,需要加入噪音的数值向量: ·factor:数值型: ·amount: ...