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 ...
随机推荐
- 一种将历史地图坐标配准到GIS中的方法
经常我们看到历史地图影像,比如谭图里面的各个历史朝代的大地图, 然后我们希望利用这个影像作为图层或者叫底图,然后在GIS软件上编辑一些矢量文件, 从而产生的地图矢量文件具有真实的经纬度坐标,不是单单的 ...
- 有分类有tag
1 2
- ffmpeg-5.0-essentials_build 下载
ffmpeg-5.0-essentials_build下载放到蓝奏里了 https://wwz.lanzoub.com/if9xq02pttkb密码:ee8i
- deepseek:以php为例,获取令牌后下一步处理步骤
在 PHP 中,获取到 Bearer Token 后,下一步通常是验证令牌的有效性,并根据令牌中的信息处理请求.以下是详细的步骤和代码示例: 1. 获取 Authorization 头中的令牌 首先, ...
- 什么是CPU?
当你用手机刷短视频.用电脑玩游戏,或是使用智能手表查看健康数据时,这些设备的核心"大脑"--CPU(中央处理器)正在默默工作.它是现代计算设备的核心,但很多人对它一知半解.今天我们 ...
- C#/.NET/.NET Core优秀项目和框架2025年2月简报
前言 公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的详细介绍.功能特点.使用方式以及部分功能 ...
- [tldr]通过指令获取github仓库的单个文件的内容
针对一个公开的github仓库,有些时候不需要clone整个仓库的内容,只需要对应的几个文件.但是直接通过网页点击下载文件很麻烦,在服务器上也不好这样操作. 因此,如何使用curl或者wget指令快速 ...
- linux中如何判断一个rpm是手动安装还是通过yum安装的
现状 对于一个不熟悉的服务器或者是虽然是自己的服务器,但历史比较久远,对于上面安装了的一些软件包,我们记忆都慢慢模糊了. 我今天遇到一个情况,在安装一个工具x2openEuler时,安装失败,提示依赖 ...
- linux mongodb安装
更新依赖包 安装前我们需要安装各个 Linux 平台依赖包. Red Hat/CentOS: sudo yum install libcurl openssl Ubuntu 18.04 LTS (&q ...
- linux安装protoc
protobuf 是做什么的? 专业的解答: Protocol Buffers 是一种轻便高效的结构化数据存储格式,可用于结构化数据串行化,很适合做数据存储或 RPC 数据交换格式.它可用于通讯协议. ...