Python基础 - 微线程
也是多任务系列哦, 进程, 线程, 微线程, 这样一来, 对于多任务这个话题, 应该算是有所涉猎了吧. 我也不怎用其实.
微线程, 就是在 单线程的前提下, 完成多任务, 多任务按照一定顺序交替执行. 明明有多线程, 为啥又要提下微线程呢? 这也是因为我用的解释器是 CPython, 它的设计就是单线程的. 即在 CPython 中, 任务都是单线程的, 这是个历史问题, 不想去深究. 总之, 这也是我一旦遇到多任务的场景时, 优先选择多进程, 进程池这些, 比较高效稳定的方式, 尽管其资源开销大 (但机器好呀, 还行吧).
与其说多任务中, 个人更偏向多进程, 倒不如说, CPython 并未实现多线程, 这也没啥可选的. 而日常任务也更多是单线程的, 如何让单线程跑得更快 那个优先点, 就是咱要谈的 微线程.
微线程实现 - yield
关于 yield 在生成器的笔记中已经谈论过了, 着重理解在一个函数中, 跟 return 的区别即可.
- return 会直接停止掉该函数的运行.
- 在函数中有 yield 会 反复被执行, 每遇到 yield 就返回值, 直到遇见 return 或 异常则终止.
详见 生成器: https://www.cnblogs.com/chenjieyouge/p/12285545.html
def task_01():
while True:
print("task_01 is working...")
yield 666
def task_02():
while True:
print("task_02 is working...")
yield 999
if __name__ == '__main__':
t1 = task_01()
t2 = task_02()
while True:
next(t1)
next(t2)
task_01 is working...
task_02 is working...
task_01 is working...
task_02 is working...
task_01 is working...
task_02 is working...
...
这样就简单实现了多任务的不断不断交替执行呀. 但这样我手写的方式, 肯定是不太高效的. Python有相关的第三方库呀, 啥都有, 然后给推荐一波常用的 gevent 贼好用呢.
gevent - 微线程
它是对 greenlet 模块的封装, 如果之前有听说过这个包的话. 没有也无所谓, 能学会基本使用就行啦. 它的特点是, 当 greenlet 遇到比较耗时的 IO 操作的时候, 就自动切换到 gevent. 然后等到 IO 操作完成后, 再切回来继续运行.
IO : 即 input 与 output. 如网络请求任务, 文件读写等这类的比较费时的任务.
基本使用
import time
import gevent
def task_01(n):
for i in range(n):
print(f"task_01 is working ...{i}")
# 模拟 IO 耗时, 让其自动切换
gevent.sleep(1)
def task_02(n):
for i in range(n):
print(f"task_02 is working ...{i}")
if __name__ == '__main__':
g1 = gevent.spawn(task_01, 3)
g2 = gevent.spawn(task_02, 4)
gevent.joinall([g1, g2])
task_01 is working ...0
task_02 is working ...0
task_02 is working ...1
task_02 is working ...2
task_02 is working ...3
task_01 is working ...1
task_01 is working ...2
在上述代码执行的过程中,我们人为使用 gevent.sleep(0)创建了一个阻塞,gevent在运行到这里时就会自切换函数。也可以在执行的时候sleep更长时间,可以发现两个函数基本是同时运行然后各自等待。
打补丁 - 自动识别 IO
gevent 里面有个 monkey 模块, 通过 monkey.patch_all () 来自动识别 IO 操作. 咋一听, 自动识别, 听厉害, 但仔细一想, 不就是挨个去判断什么 sys, os, request, htttp ... 这些嘛. 源码大致这样的.
# 核心的一段哈
# order is important
if os:
patch_os()
if time:
patch_time()
if thread:
patch_thread(Event=Event, _warnings=_warnings)
# sys must be patched after thread. in other cases threading._shutdown will be
# initiated to _MainThread with real thread ident
if sys:
patch_sys()
if socket:
patch_socket(dns=dns, aggressive=aggressive)
if select:
patch_select(aggressive=aggressive)
if ssl:
patch_ssl(_warnings=_warnings, _first_time=first_time)
if httplib:
raise ValueError('gevent.httplib is no longer provided, httplib must be False')
if subprocess:
patch_subprocess()
if builtins:
patch_builtins()
if signal:
patch_signal()
if queue:
patch_queue()
...
不过还可以了. 如果咱还有其没有覆盖到地方, 那可以去 继承, 然后重写呀. 在刚刚那个例子就可以不用 手动去sleep 因为打了补丁了呀.
import time
import gevent
from gevent import monkey
# 给程序打补丁, 让其自动识别 IO 操作
monkey.patch_all()
def task_01(n):
for i in range(n):
print(f"task_01 is working ...{i}")
def task_02(n):
for i in range(n):
print(f"task_02 is working ...{i}")
if __name__ == '__main__':
g1 = gevent.spawn(task_01, 3)
g2 = gevent.spawn(task_02, 4)
gevent.joinall([g1, g2])
task_01 is working ...0
task_01 is working ...1
task_01 is working ...2
task_02 is working ...0
task_02 is working ...1
task_02 is working ...2
task_02 is working ...3
这个补丁 monkey, 确实可以. 我感觉这些老外, 取名字也挺有趣的, 特喜欢用动物的名称.
栗子 - 小爬虫
import urllib.request
import gevent
from gevent import monkey
monkey.patch_all()
def get_image(url_path, file_name):
try:
response = urllib.request.urlopen(url_path)
with open(file_name, "wb") as f:
while True:
image_data = response.read(1024)
if image_data:
f.write(image_data)
else:
break
except:
print("connect fail")
else:
print("success")
if __name__ == '__main__':
# 没有真的, 主要是 url 太长了, 影响我排版, 就不搞了.
image_01 = "https://xxxx.jpg"
image_02 = "https://xxxxjpg"
image_03 = "http://www.xxxxp3"
# 创建多个微线程 gevent
g1 = gevent.spawn(get_image, image_01, "01.jpg")
g2 = gevent.spawn(get_image, image_02, "02.jpg")
g3 = gevent.spawn(get_image, image_03, "03.jpg")
gevent.joinall([g1, g2, g3])
小结
- 一个进程, 至少有一个线程; 一个线程里, 可以有多个 微线程
- 微线程, 是在单线程中的, 可以用 yield 模拟, 推荐用 gevent (别忘 mokey.patch_all) 自动识别 IO
- 进程是OS 调度的基本单位, 多线程在 CPython 中并未真正实现哦, 故多线程我基本不用
- 多线程资源开销大, 但我常用, 我就喜欢浪费资源, 你管我呢. 而微线程基于单线程, 开销很小, 我感觉可以多用用
这样一来, 进程, 线程, 微线程, 一组合起来用, 就蛮有趣的. 我是很少用, 前段时间, 倒是看到我小伙伴用, 多进程 + 微线程, 看他跑任务的时候, 还是蛮酷的哦.
Python 基础的多任务, 感觉差不多了. 嗯, 基础部分感觉也就这些. 哦, 还差一点 网络编程... 除外也没啥东西了, 编程语言的也基本就这些, 一言隐蔽之.
变量 -> 表达式 -> 控制流 -> 基础数据结构 -> 函数 -> 面向(过程) 对象 -> IO -> 网络编程 ....
Python基础 - 微线程的更多相关文章
- Python 基础之 线程与进程
Python 基础之 线程与进程 在前面已经接触过了,socket编程的基础知识,也通过socketserver 模块实现了并发,也就是多个客户端可以给服务器端发送消息,那接下来还有个问题,如何用多线 ...
- python基础之线程、进程、协程
线程 线程基础知识 一个应用程序,可以多进程.也可以多线程. 一个python脚本,默认是单进程,单线程的. I/O操作(音频.视频.显卡操作),不占用CPU,所以: 对于I/O密集型操作,不会占用C ...
- 【Python之路】第九篇--Python基础之线程、进程和协程
进程与线程之间的关系 线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除.线程可与属于同一进程的其它线程共享进程所拥有的全 ...
- python基础(34):线程(二)
1. python线程 1.1 全局解释器锁GIL Python代码的执行由Python虚拟机(也叫解释器主循环)来控制.Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行.虽然 Py ...
- python基础(33):线程(一)
1. 线程概念的引入背景 1.1 进程 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在 ...
- python基础之 线程_进程关系
上图
- Python并发编程-线程同步(线程安全)
Python并发编程-线程同步(线程安全) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 线程同步,线程间协调,通过某种技术,让一个线程访问某些数据时,其它线程不能访问这些数据,直 ...
- Py修行路 python基础 (二十五)线程与进程
操作系统是用户和硬件沟通的桥梁 操作系统,位于底层硬件与应用软件之间的一层 工作方式:向下管理硬件,向上提供接口 操作系统进行切换操作: 把CPU的使用权切换给不同的进程. 1.出现IO操作 2.固定 ...
- python基础-12 多线程queue 线程交互event 线程锁 自定义线程池 进程 进程锁 进程池 进程交互数据资源共享
Python中的进程与线程 学习知识,我们不但要知其然,还是知其所以然.你做到了你就比别人NB. 我们先了解一下什么是进程和线程. 进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CP ...
- python基础(16)-进程&线程&协程
进程之multiprocessing模块 Process(进程) Process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建. 介绍 初始化参数 Process([group [, t ...
随机推荐
- python接入百度智能云API实现ai对话
python接入百度智能云API实现ai对话 千帆大模型平台-百度智能云千帆 代码段: import requests import json # 获取访问令牌的函数 def get_access_t ...
- Mybatis之Select Count(*)的获取 返回int 的值
本文将介绍,SSM中mybatis 框架如何获取Select Count(*)返回int 的值.1. Service 代码: public boolean queryByunitclass(Strin ...
- 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
现在使用Uni-app开发手机端APP已经变得很普遍,同一套代码就可以打包成Android App 和 iOS App,相比原生开发,可以节省客观的人力成本.那么如何使用Uni-app来开发视频聊天软 ...
- 文件快递柜FileCoxBox-匿名口令分享文本,文件,像拿快递一样取文件
FileCoxBox特色 轻量简洁:Fastapi+Sqlite3+Vue2+ElementUI 轻松上传:复制粘贴,拖拽选择 多种类型:文本,文件 防止爆破:错误次数限制 防止滥用:IP限制上传次数 ...
- 超级详细的mysql数据库安装指南
https://zhuanlan.zhihu.com/p/37152572 2,073 人赞同了该文章 如果你的电脑是mac,参考社群会员 @奔跑的土豆 的分享: mac下mysql的安装步骤 227 ...
- 当你在浏览器中输入 google.com 后按下回车发生了什么?
按下"g"键 接下来的内容介绍了物理键盘和系统中断的工作原理,但是有一部分内容却没有涉及.当你按下"g"键,浏览器接收到这个消息之后,会触发自动完成机制.浏览器 ...
- python包管理工具pip使用手册
pip是什么? pip 是 Python 标准库管理器,也就是一个工具让你安装不同的类库来使用. 当你要安装某些类库时,都会使用 pip,那 pip 除了安装类库之外,还能做什么呢? 首先,我们进入 ...
- golang gin框架使用swagger生成接口文档
前言 一份清晰明了的接口文档能够极大地提高前后端双方的沟通效率和开发效率. 本文将介绍如何使用swagger生成接口文档. swagger介绍 Swagger本质上是一种用于描述使用JSON表示的RE ...
- 数据库自增 ID 用完了会怎么样?
前言 数据库中的自增 ID 用完了该怎么办? 这个问题可以分为有主键 & 无主键两种情况回答. 有主键 如果你的表有主键,并且把主键设置为自增. 在 MySQL 中,一般会把主键设置成 int ...
- IvorySQL 升级指南:从 3.x 到 4.0 的平滑过渡
日前,IvorySQL 4.0 重磅发布,全面支持 PostgreSQL 17,并且增强了对 Oracle 的兼容性.关于 IvorySQL 4.0 的介绍,各位小伙伴可以通过这篇文章回顾:Ivory ...