Python协程详解:从基础到实战
协程是Python中实现并发编程的重要方式之一,它比线程更轻量级,能够高效处理I/O密集型任务。本文将全面介绍协程的概念、原理、实现方式以及与线程、进程的对比,包含完整的效率对比代码和详细说明,帮助Python开发者深入理解并掌握协程技术。
1. 协程基础概念
1.1 什么是协程
协程(Coroutine)是Python中另外一种实现多任务的方式,它是一种比线程更小的执行单元,占用更少的资源。协程之所以被称为执行单元,是因为它自带CPU上下文。这意味着只要在合适的时机,我们可以把一个协程切换到另一个协程,只要在这个过程中保存或恢复CPU上下文,程序就可以继续运行。
通俗地说:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行(注意不是通过调用函数的方式做到的),并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定。
1.2 为什么需要协程
在传统多线程编程中,我们知道:
-
进程是资源分配的最小单位
-
线程是CPU调度的最小单位
虽然多线程已经提高了CPU利用率,但创建和管理线程/进程需要消耗系统资源。随着对效率的追求不断提高,基于单线程实现并发成为一个新的课题,即只用一个主线程(很明显可利用的CPU只有一个)情况下实现并发。这样可以节省创建线程/进程所消耗的时间。
1.3 并发的本质
并发的本质可以概括为:切换+保存状态
CPU正在运行一个任务,会在两种情况下切走去执行其他的任务(切换由操作系统强制控制):
-
该任务发生了阻塞(如I/O操作)
-
该任务计算的时间过长(时间片用完)
其中第二种情况并不能提升效率,只是为了让CPU能够"雨露均沾",实现看起来所有任务都被"同时"执行的效果。如果多个任务都是纯计算的,这种切换反而会降低效率。
2. 协程与线程的差异
在实现多任务时,线程切换从系统层面远不止保存和恢复CPU上下文这么简单。操作系统为了程序运行的高效性,每个线程都有自己缓存Cache等数据,操作系统还会帮你做这些数据的恢复操作。所以线程的切换非常耗性能。
相比之下,协程的切换只是单纯地操作CPU的上下文,所以一秒钟切换个上百万次系统都抗得住。具体差异如下:
特性 | 线程 | 协程 |
---|---|---|
调度者 | 操作系统 | 用户程序 |
切换代价 | 高(涉及内核态切换) | 低(用户态切换) |
内存占用 | 较大(MB级) | 极小(KB级) |
并发数量 | 有限(千级) | 极高(百万级) |
数据同步 | 需要锁机制 | 无需加锁 |
3. Python协程的特点
-
必须在单线程里实现并发:协程的本质是单线程下的并发
-
修改共享数据不需加锁:因为是单线程,不存在竞争条件
-
用户程序自己保存多个控制流的上下文栈
-
自动I/O切换:一个协程遇到I/O操作自动切换到其它协程(需要gevent等模块支持)
4. Python协程的实现方式
4.1 yield模拟协程
Python中的生成器(generator)和yield关键字可以初步模拟协程的行为:
def consumer():while True:x = yield # 接收send发送的值print(f"处理数据: {x}")def producer():c = consumer()c.__next__() # 启动生成器for i in range(5):print(f"生产数据: {i}")c.send(i) # 发送数据给consumerproducer()
这段代码展示了生产者-消费者模型的基本协程实现。生产者通过send()方法将数据发送给消费者,消费者通过yield接收并处理数据。
4.2 Greenlet模块
Greenlet是一个更专业的协程实现,它提供了明确的切换方法:
from greenlet import greenlet
import timedef task1():print("执行任务1")gr2.switch() # 切换到任务2print("任务1继续执行")gr2.switch()def task2():print("执行任务2")gr1.switch() # 切换回任务1print("任务2继续执行")gr1 = greenlet(task1)
gr2 = greenlet(task2)
gr1.switch() # 启动任务1
Greenlet的优点是切换明确,缺点是需要手动切换,无法自动识别I/O阻塞。
4.3 Gevent模块
Gevent是基于Greenlet的更高级协程库,它能够自动处理I/O阻塞:
import gevent
from gevent import monkey
monkey.patch_all() # 打补丁,替换标准库中的阻塞式I/Odef fetch(url):print(f"获取 {url}")gevent.sleep(2) # 模拟I/O操作print(f"{url} 返回数据")def async_fetch():urls = ['url1', 'url2', 'url3']jobs = [gevent.spawn(fetch, url) for url in urls]gevent.joinall(jobs)async_fetch()
Gevent的关键点:
-
monkey.patch_all()
:替换Python标准库中的阻塞式I/O为Gevent的非阻塞版本 -
gevent.spawn()
:创建协程任务 -
gevent.joinall()
:等待所有协程完成
5. 协程效率对比测试
5.1 顺序执行 vs 协程执行
下面我们通过一个完整的例子来对比顺序执行和协程执行的效率差异:
from gevent import monkey
import gevent
import time
import requests# 打补丁
monkey.patch_all()# 测试URL
url = "https://www.baidu.com"# 顺序执行
def sequential_fetch():start = time.time()for i in range(5):print(f"顺序请求 {i} 开始")response = requests.get(url)print(f"顺序请求 {i} 完成,状态码: {response.status_code}")print(f"顺序执行总耗时: {time.time() - start:.2f}秒")# 协程执行
def coroutine_fetch():start = time.time()def fetch(i):print(f"协程请求 {i} 开始")response = requests.get(url)print(f"协程请求 {i} 完成,状态码: {response.status_code}")jobs = [gevent.spawn(fetch, i) for i in range(5)]gevent.joinall(jobs)print(f"协程执行总耗时: {time.time() - start:.2f}秒")# 执行测试
print("=== 顺序执行测试 ===")
sequential_fetch()print("\n=== 协程执行测试 ===")
coroutine_fetch()
执行结果分析:
-
顺序执行:每个请求依次执行,总耗时≈各请求耗时之和
-
协程执行:所有请求并发执行,总耗时≈最慢的单个请求耗时
5.2 线程 vs 协程效率对比
下面我们对比线程和协程在处理I/O密集型任务时的效率:
import threading
import time
import requests
from gevent import monkey
import geventmonkey.patch_all()
url = "https://www.baidu.com"
n = 10 # 并发数量# 线程方式
def thread_fetch():start = time.time()def fetch(i):response = requests.get(url)print(f"线程 {i} 完成")threads = []for i in range(n):t = threading.Thread(target=fetch, args=(i,))t.start()threads.append(t)for t in threads:t.join()print(f"线程方式总耗时: {time.time() - start:.2f}秒")# 协程方式
def gevent_fetch():start = time.time()def fetch(i):response = requests.get(url)print(f"协程 {i} 完成")jobs = [gevent.spawn(fetch, i) for i in range(n)]gevent.joinall(jobs)print(f"协程方式总耗时: {time.time() - start:.2f}秒")# 执行测试
print("=== 线程方式测试 ===")
thread_fetch()print("\n=== 协程方式测试 ===")
gevent_fetch()
执行结果分析:
-
线程方式:创建线程有一定开销,线程切换需要内核参与
-
协程方式:协程创建和切换开销极小,完全在用户空间完成
5.3 不同并发量下的效率对比
我们测试不同并发量下协程的执行效率:
import time
from gevent import monkey
import gevent
import requestsmonkey.patch_all()def test_concurrency(concurrency):print(f"\n=== 并发数: {concurrency} ===")start = time.time()def task(i):requests.get("https://www.baidu.com")print(f"任务 {i} 完成", end=" | ")jobs = [gevent.spawn(task, i) for i in range(concurrency)]gevent.joinall(jobs)elapsed = time.time() - startprint(f"\n并发数 {concurrency} 总耗时: {elapsed:.2f}秒")return elapsed# 测试不同并发量
concurrency_levels = [10, 50, 100, 200, 500]
results = {}
for level in concurrency_levels:results[level] = test_concurrency(level)# 打印结果对比
print("\n=== 结果汇总 ===")
for level, time_cost in results.items():print(f"并发数 {level}: {time_cost:.2f}秒")
预期结果:
-
小并发量时总耗时接近单个请求耗时
-
随着并发量增加,总耗时增长缓慢
-
协程可以轻松支持上千并发
6. 协程的实际应用
6.1 网络爬虫
协程特别适合网络爬虫这种I/O密集型应用:
import gevent
from gevent import monkey
monkey.patch_all()
import requests
from urllib.parse import urljoin
from bs4 import BeautifulSoupbase_url = "https://www.example.com"
visited = set()def crawler(url):if url in visited:returnvisited.add(url)try:print(f"抓取: {url}")response = requests.get(url)soup = BeautifulSoup(response.text, 'html.parser')# 处理页面内容...print(f"从 {url} 找到 {len(soup.find_all('a'))} 个链接")# 发现新链接links = [urljoin(base_url, a['href']) for a in soup.find_all('a', href=True)]# 创建协程抓取新链接jobs = [gevent.spawn(crawler, link) for link in links if link.startswith(base_url)]gevent.joinall(jobs)except Exception as e:print(f"抓取 {url} 出错: {e}")# 开始抓取
crawler(base_url)
6.2 Web服务器
使用协程可以轻松实现高并发的Web服务器:
from gevent.pywsgi import WSGIServer
from gevent import monkey
monkey.patch_all()def application(env, start_response):path = env['PATH_INFO']if path == '/':start_response('200 OK', [('Content-Type', 'text/html')])return [b"<h1>Welcome</h1><p>Hello from Gevent server!</p>"]elif path == '/api/data':# 模拟数据库查询gevent.sleep(1)start_response('200 OK', [('Content-Type', 'application/json')])return [b'{"data": [1, 2, 3]}']else:start_response('404 Not Found', [('Content-Type', 'text/html')])return [b"<h1>404 Not Found</h1>"]if __name__ == '__main__':print("Server running on http://localhost:8000")server = WSGIServer(('0.0.0.0', 8000), application)server.serve_forever()
6.3 数据库操作
协程化的数据库操作可以显著提高并发性能:
import gevent
from gevent import monkey
monkey.patch_all()
import pymysql
from DBUtils.PooledDB import PooledDB# 创建数据库连接池
db_pool = PooledDB(creator=pymysql,host='localhost',user='root',password='123456',db='test',maxconnections=20
)def query_user(user_id):conn = db_pool.connection()try:with conn.cursor() as cursor:sql = "SELECT * FROM users WHERE id = %s"cursor.execute(sql, (user_id,))result = cursor.fetchone()print(f"查询到用户 {user_id}: {result}")return resultfinally:conn.close()# 并发查询多个用户
user_ids = [1, 2, 3, 4, 5]
jobs = [gevent.spawn(query_user, uid) for uid in user_ids]
gevent.joinall(jobs)
7. 协程与多线程、多进程的对比
7.1 完整对比表格
特性 | 进程 | 线程 | 协程 |
---|---|---|---|
创建开销 | 大(10+MB) | 中(1MB左右) | 极小(几KB) |
切换开销 | 高(微秒级) | 中(微秒级) | 低(纳秒级) |
内存占用 | 独立内存空间 | 共享进程内存 | 共享线程内存 |
数据共享 | IPC机制 | 全局变量 | 全局变量 |
并发数量 | 数十个 | 数百个 | 数十万个 |
CPU利用 | 多核 | 多核(受GIL限制) | 单核 |
适用场景 | CPU密集型 | I/O+少量CPU | I/O密集型 |
编程复杂度 | 高 | 中(需同步) | 低 |
稳定性 | 高(隔离性好) | 中(一个线程崩溃影响整个进程) | 中(一个协程出错可能影响其他协程) |
7.2 性能对比测试
下面我们通过一个完整的测试来对比进程、线程和协程的性能差异:
import time
import multiprocessing
import threading
from gevent import monkey
import geventmonkey.patch_all()# 测试任务:模拟I/O密集型操作
def task(n):gevent.sleep(1) # 模拟I/O操作return n * n# 进程方式
def process_way():start = time.time()pool = multiprocessing.Pool(processes=10)results = pool.map(task, range(10))pool.close()pool.join()print(f"进程方式 结果: {results} 耗时: {time.time() - start:.2f}秒")# 线程方式
def thread_way():start = time.time()threads = []results = []def worker(n):results.append(task(n))for i in range(10):t = threading.Thread(target=worker, args=(i,))t.start()threads.append(t)for t in threads:t.join()print(f"线程方式 结果: {results} 耗时: {time.time() - start:.2f}秒")# 协程方式
def coroutine_way():start = time.time()jobs = [gevent.spawn(task, i) for i in range(10)]gevent.joinall(jobs)results = [job.value for job in jobs]print(f"协程方式 结果: {results} 耗时: {time.time() - start:.2f}秒")# 执行测试
print("=== 进程方式测试 ===")
process_way()print("\n=== 线程方式测试 ===")
thread_way()print("\n=== 协程方式测试 ===")
coroutine_way()
预期结果:
-
进程方式:耗时≈1秒,但创建进程开销大
-
线程方式:耗时≈1秒,受GIL影响
-
协程方式:耗时≈1秒,但资源占用最少
8. 协程最佳实践
8.1 避免CPU密集型任务
协程不适合纯CPU计算任务,下面是一个反面例子:
import time
from gevent import monkey
import geventmonkey.patch_all()# CPU密集型任务
def cpu_intensive(n):result = 0for i in range(n):result += i * ireturn result# 测试协程执行CPU密集型任务
def test_coroutine_cpu():start = time.time()jobs = [gevent.spawn(cpu_intensive, 1000000) for _ in range(10)]gevent.joinall(jobs)print(f"协程执行CPU密集型任务耗时: {time.time() - start:.2f}秒")# 测试顺序执行
def test_sequential_cpu():start = time.time()results = [cpu_intensive(1000000) for _ in range(10)]print(f"顺序执行CPU密集型任务耗时: {time.time() - start:.2f}秒")print("=== CPU密集型任务测试 ===")
test_coroutine_cpu()
test_sequential_cpu()
结论:对于CPU密集型任务,协程不会带来性能提升,反而可能因为切换开销而降低性能。
8.2 合理控制并发量
虽然协程很轻量,但也需要合理控制并发量:
from gevent import monkey
import gevent
import requests
import timemonkey.patch_all()def fetch(url, semaphore):with semaphore: # 控制并发量print(f"开始请求 {url}")response = requests.get(url)print(f"完成请求 {url} 状态码: {response.status_code}")def controlled_concurrency(url, concurrency=10, total=100):start = time.time()semaphore = gevent.pool.Semaphore(concurrency) # 并发信号量jobs = [gevent.spawn(fetch, url, semaphore) for _ in range(total)]gevent.joinall(jobs)print(f"总请求数: {total} 并发数: {concurrency} 总耗时: {time.time() - start:.2f}秒")# 测试不同并发控制
url = "https://www.baidu.com"
controlled_concurrency(url, concurrency=10, total=100)
controlled_concurrency(url, concurrency=20, total=100)
controlled_concurrency(url, concurrency=50, total=100)
8.3 异常处理
正确处理协程中的异常:
import gevent
from gevent import monkey
monkey.patch_all()def successful_task():return "成功完成任务"def failing_task():raise Exception("任务执行失败")def handle_exceptions():jobs = [gevent.spawn(successful_task),gevent.spawn(failing_task),gevent.spawn(successful_task)]gevent.joinall(jobs)for job in jobs:if job.successful():print(f"任务结果: {job.value}")else:print(f"任务失败: {job.exception}")print("=== 异常处理测试 ===")
handle_exceptions()
9. 常见问题解答
Q: 协程能利用多核CPU吗?
A: 单个协程不能,因为它在单线程中运行。但可以通过多进程+协程的方式利用多核,例如每个进程运行一个事件循环。
Q: 协程会取代线程吗?
A: 不会完全取代。协程适合I/O密集型场景,线程适合需要利用多核或与C扩展交互的场景。最佳实践是根据需求选择合适的并发模型。
Q: Gevent的monkey patch安全吗?
A: 在大多数情况下是安全的,但应注意:
-
尽早调用
monkey.patch_all()
-
避免与其他修改标准库的库同时使用
-
生产环境中先充分测试
Q: 如何调试协程程序?
A: 调试协程程序可以使用:
-
打印日志
-
gevent.getcurrent()
获取当前协程 -
使用支持协程的调试器如PyCharm专业版
10. 总结
协程是Python中强大的并发编程工具,特别适合I/O密集型应用。通过本文的完整介绍和代码示例,我们了解了:
-
协程的基本概念和原理
-
Python中实现协程的三种方式:yield、greenlet、gevent
-
完整的效率对比测试代码和结果分析
-
协程在实际项目中的应用场景
-
协程与线程、进程的详细对比
-
协程编程的最佳实践和常见问题
掌握协程技术可以让你的Python程序在处理高并发时更加高效和优雅。希望本文能帮助你全面理解Python协程,并在实际项目中灵活应用。
相关文章:
Python协程详解:从基础到实战
协程是Python中实现并发编程的重要方式之一,它比线程更轻量级,能够高效处理I/O密集型任务。本文将全面介绍协程的概念、原理、实现方式以及与线程、进程的对比,包含完整的效率对比代码和详细说明,帮助Python开发者深入理解并掌握协…...
服务器部署LLaMAFactory进行LoRA微调
一、什么是LLaMAFactory LlamaFactory 是一个专为 大型语言模型(LLM)微调 设计的开源工具库,旨在简化大模型(如 LLaMA、GPT、Mistral 等)的定制化训练流程,降低技术门槛和硬件成本。以下是它的核心功能和应…...
ASP.NET MVC 入门指南
以下是一份 MVC(Model - View - Controller)培训教程,以ASP.NET MVC 为例进行讲解,适合有一定编程基础的学习者快速上手。 1. MVC 概述 1.1 什么是 MVC MVC 是一种软件设计模式,它将应用程序分为三个主要部分&#…...
mapbox高阶,高程影像、行政区边界阴影效果实现
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️line线图层样式1.4 ☘️symbol符号图层…...
如何下载适用于语音识别功能增强的Google Chrome浏览器
谷歌浏览器一直是互联网用户的首选工具之一,尤其是它强大的扩展功能,使得用户可以根据需求定制浏览器。对于需要使用语音识别功能的用户来说,谷歌浏览器提供了优秀的支持,通过简单的设置和插件,可以显著提升语音识别的…...
运维打铁:Centos 7 安装 redis_exporter 1.3.5
文章目录 一、CentOS 7 安装 redis_exporter 1.3.51. 安装2. 配置自启动,并连接 Redis,修改端口3. 配置 Prometheus 采集 redis_exporter 数据4. 配置 Grafana 查看数据5. Redis 集群配置 二、常见问题及解决办法1. 下载二进制包失败2. 解压部署时权限问…...
3台CentOS虚拟机部署 StarRocks 1 FE+ 3 BE集群
背景:公司最近业务数据量上去了,需要做一个漏斗分析功能,实时性要求较高,mysql已经已经不在适用,做了个大数据技术栈选型调研后,决定使用StarRocks StarRocks官网:StarRocks | A High-Performa…...
Oracle 11g RAC ASM磁盘组剔盘、加盘实施过程
环境:AIX6.1 Oracle RAC 11.2.0.3 前期准备: 1.查看DG磁盘组空间情况: –查看DG磁盘组空间情况: ASMCMD> lsdg State Type Rebal Sector Block AU Total_MB Free_MB Req_mir_free_MB Usable_file_MB Of…...
网站高可用架构设计基础——高可用策略和架构原则
一、正面保障与减少损失 要想让系统能够稳定可用,首先要考虑如何避免问题的发生。比如说可以通过 UPS(不间断电源)来避免服务器断电,可以通过事先增加机器来解决硬件资源不足的问题。 然后,如果问题真的发生了&#…...
从入门到精通【MySQL】视图与用户权限管理
文章目录 📕1. 视图✏️1.1 视图的基本概念✏️1.2 试图的基本操作🔖1.2.1 创建视图🔖1.2.2 使用视图🔖1.2.3 修改数据🔖1.2.4 删除视图 ✏️1.3 视图的优点 📕2. 用户与权限管理✏️2.1 用户🔖…...
使用QML Tumbler 实现时间日期选择器
目录 引言相关阅读项目结构示例实现与代码解析示例一:时间选择器(TimePicker)示例二:日期时间选择器(DateTimePicker) 主窗口整合运行效果总结下载链接 引言 在现代应用程序开发中,时间与日期选…...
[golang] 介绍 | 特点 | 应用场景
“编程不仅仅是写代码,更是一种思考方式。” 参考资料 《Unix编程环境》- Brian W. Kernighan, Rob Pike《程序设计实践》- Brian W. Kernighan, Rob PikeGo语言官方网站:https://golang.orgRob Pike的个人博客:http://herpolhode.com/rob/ …...
Python 爬虫实战 | 企名科技
文章目录 一、企名科技1、目标网站2、网站特点3、确定解密位置4、扣js代码 一、企名科技 1、目标网站 网址:https://wx.qmpsee.com/articleDetail?idfeef62bfdac45a94b9cd89aed5c235be目标数据:获取消费行业研究下面的13篇文章数据 2、网站特点 服…...
c加加学习之day06->STL标准库->day01
1.介绍:C 标准模板库(Standard Template Library,简称 STL)是一组泛型编程的模板类和函数,旨在提供常用的数据结构、算法和函数对象。STL 是 C 标准库的一部分,极大地提高了编程效率和代码的可重用性。STL …...
并发设计模式实战系列(6):读写锁
🌟 大家好,我是摘星! 🌟 今天为大家带来的是并发设计模式实战系列,第六章读写锁模式,废话不多说直接开始~ 目录 一、核心原理深度拆解 1. 读写锁三维模型 2. 关键实现原理 二、生活化类比&am…...
【网络原理】从零开始深入理解TCP的各项特性和机制.(一)
本篇博客给大家带来的是网络原理的相关知识.其中传输层这一部分非常重要,面试中只要是涉及到网络这一部分知识,几乎是必定会考传输层TCP的. 🐎文章专栏: JavaEE初阶 🚀若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给…...
基于Pytorch的深度学习-第二章
2.1 CIFAR-10数据集简介 CIFAR-10数据集包含10个类别:plane、car、bird、cat、deer、dog、frog、horse、ship、truck,每个类别有6000张图片。其中训练集图片有50000张,测试集有10000张图片。训练集和测试集的生成方法是,分别从每…...
gitlab-ce容器镜像源(国内)
下载命令 docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/gitlab/gitlab-ce:17.10.4-ce.0 更多参考: https://docker.aityp.com/image/docker.io/gitlab/gitlab-ce:17.10.4-ce.0...
TinyVue v3.22.0 正式发布:深色模式上线!集成 UnoCSS 图标库!TypeScript 类型支持全面升级!
我们非常高兴地宣布,2025年4月7日,TinyVue发布了v3.22.0🎉。 本次 3.22.0 版本主要有以下重大变更: 支持深色模式增加基于 UnoCSS 的图标库更丰富的 TypeScript 类型声明支持 XSS 配置 详细的 Release Notes 请参考:…...
Browser-Use WebUI:让AI自动使用浏览器帮你查询信息执行任务
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
使用PyTorch如何配置一个简单的GTP
目录 一、什么是GPT 1. Transformer Block 的核心结构 2. 关键组件解析 (1) 掩码多头自注意力(Masked Multi-Head Self-Attention) (2) 前馈神经网络(FFN) (3) 层归一化(LayerNorm&…...
【FAQ】针对于消费级NVIDIA GPU的说明
概述 本文概述 HP Anyware 在配备消费级 NVIDIA GPU 的物理工作站上的关键组件、安装说明和重要注意事项。 注意:本文档适用于 NVIDIA 消费级 GPU。NVIDIA Quadro 和 Tesla GPU 也支持 HP Anyware 在公有云、虚拟化或物理工作站环境中运行。请参阅PCoIP Graphi…...
02_java的运行机制以及JDKJREJVM基本介绍
1、运行机制 2、JDK&JRE&JVM JDK 基本介绍 (1) JDK 的全称(Java Development Kit Java开发工具包) JDK JRE java的开发工具 [ java, javac, javadoc, javap等 ] (2)JDK是提供给Java开发人员使用的,其…...
go 的 net 包
目录 一、net包的基本功能 1.1 IP地址处理 1.2 网络协议支持 1.3 连接管理 二、net包的主要功能模块 2.1 IP地址处理 2.2 TCP协议 2.3 UDP协议 2.4 Listener和Conn接口 三、高级功能 3.1 超时设置 3.2 KeepAlive控制 3.3 获取连接信息 四、实际应用场景 4.1 Web服…...
ShenNiusModularity项目源码学习(21:ShenNius.Admin.Mvc项目分析-6)
菜单列表页面用于新建、维护及删除系统所有模块所需的菜单信息,包括菜单名称、菜单中的按钮、菜单关联的后台服务地址及请求方式等。菜单列表页面的后台控制器类MenuController位于ShenNius.Admin.Mvc项目的Areas\Sys\Controllers内,页面文件位于同项目的…...
基于单片机的游泳馆智能管理系统
标题:基于单片机的游泳馆智能管理系统 内容:1.摘要 随着人们生活水平的提高,游泳馆的规模和客流量不断增大,传统的管理方式已难以满足高效、便捷的管理需求。本研究的目的是设计并实现一种基于单片机的游泳馆智能管理系统。方法上,采用单片机…...
开发了一个b站视频音频提取器
B站资源提取器-说明书 一、功能说明 本程序可自动解密并提取B站客户端缓存的视频资源,支持以下功能: - 自动识别视频缓存目录 - 将加密的.m4s音频文件转换为标准MP3格式 - 将加密的.m4s视频文件转换为标准MP4格式(合并音视频流)…...
vue2项目,为什么开发环境打包出来的js文件名是1.js 2.js,而生产环境打包出来的是chunk-3adddd.djncjdhcbhdc.js
Vue2项目开发环境与生产环境JS文件名差异的核心原理及配置逻辑如下: 一、文件名差异的底层机制 1、Webpack默认命名策略 开发环境默认禁用哈希,采用[id].js命名规则(如1.js),生产环境启用[chunkhash]生成chunk-xxx…...
SQL进阶知识:六、动态SQL
今天介绍下关于动态SQL的详细介绍,并结合MySQL数据库提供实际例子。 动态SQL是指在运行时动态构建和执行SQL语句的技术。这种技术在处理复杂的查询逻辑、参数化查询或在某些情况下需要根据用户输入动态调整查询时非常有用。MySQL支持动态SQL,主要通过PRE…...
Spring Boot常用注解详解:实例与核心概念
Spring Boot常用注解详解:实例与核心概念 前言 Spring Boot作为Java领域最受欢迎的快速开发框架,其核心特性之一是通过注解(Annotation)简化配置,提高开发效率。注解驱动开发模式让开发者告别繁琐的XML配置ÿ…...
java 富文本转pdf
前言: 本文的目的是将传入的富文本内容(html标签,图片)并且分页导出为pdf。 所用的核心依赖为iText7。 因为itextpdf-core的核心包在maven中央仓库中,阿里云华为云等拉不下来,中央仓库在外网,并且此包在中央仓库中未…...
17.第二阶段x64游戏实战-人工遍历二叉树结构
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 上一个内容:16.第二阶段x64游戏实战-分析二叉树结构 上一个内容里把二叉树的结构写了写&am…...
C#基于Sunnyui框架和MVC模式实现用户登录管理
C#基于Sunnyui框架和MVC模式实现用户登录管理 1 Controller1.1 UserManagementController.cs(控制器入口) 2 Model2.1 UserRepository.cs(用户管理模型)2.2 User.cs(用户结构体)2.3 SQLiteHelper.cs&#x…...
Spring Boot实战(三十六)编写单元测试
目录 一、什么是单元测试?二、Spring Boot 中的单元测试依赖三、举例 Spring Boot 中不同层次的单元测试3.1 Service层3.2 Controller 层3.3 Repository层 四、Spring Boot 中 Mock、Spy 对象的使用4.1 使用Mock对象的背景4.2 什么是Mock对象,有哪些好处…...
声音分离人声和配乐-从头设计数字生命第4课——仙盟创梦IDE
音频分离在数字人中具有多方面的重要作用,主要体现在以下几个方面: 提高语音合成质量:通过音频分离,可以将原始音频中的语音部分与其他背景噪音或干扰声音分离开来。这样在进行语音合成时,能够获得更纯净的语音信号&am…...
http协议、全站https
一、http协议 1、为何要学http协议? 用户用浏览器访问网页,默认走的都是http协议,所以要深入研究web层,必须掌握http协议 2、什么是http协议 1、全称Hyper Text Transfer Protocol(超文本传输协议) ### 一个请求得到一个响应包 普通…...
Mediamtx与FFmpeg远程与本地推拉流使用
1.本地推拉流 启服 推流 ffmpeg -re -stream_loop -1 -i ./DJI_0463.MP4 -s 1280x720 -an -c:v h264 -b:v 2000k -maxrate 2500k -minrate 1500k -bufsize 3000k -rtsp_transport tcp -f rtsp rtsp://127.0.0.1:8554/stream 拉流 ffplay -rtsp_transport tcp rtsp://43.136.…...
css3新特性第七章(3D变换)
css新特性第七章(3D变换) 一、3d空间和景深 元素进行 3D 变换的首要操作:父元素必须开启 3D 空间! 使用 transform-style 开启 3D 空间,可选值如下: flat : 让子元素位于此元素的二维平面内( 2D 空间&…...
redis经典问题
1.缓存雪崩 指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。 解决方案: 1)Redis 高可用,主从哨兵,Redis cluster,避免全盘崩…...
数据仓库是什么?数据仓库架构有哪些?
目录 数据仓库是什么?数据仓库架构有哪些? 一、数据仓库是什么? 二、数据仓库的架构分层 1. 获取层 2. 数据层 3. 应用层 4. 访问层 三、数据仓库的价值体现 1.决策支持 2.业务优化 3.提升竞争力 四、数据仓库的未来发展趋势 总…...
Nginx 通过 Let‘s Encrypt 实现 HTTPS 访问全流程指南
一、Let’s Encrypt 与 Certbot 简介 Let’s Encrypt 是由非营利组织 ISRG 运营的免费证书颁发机构(CA),旨在推动 HTTPS 的普及。其核心工具 Certbot 能自动化完成证书申请、部署与续期,大幅降低 HTTPS 的配置复杂度。通过 Certb…...
网络知识:路由器静态路由与动态路由介绍
目录 一、静态路由 1.1 什么是静态路由? 1.2 静态路由的好处 1.3 静态路由的局限 1.4 静态路由应用场景 微型办公室网络 性能要求高业务流量 安全性要求高的环境 二、动态路由 2.1 什么是动态路由? 2.2 动态路由的好处 2.3 动态路由的局限 2.4 动态路由的应用场…...
LLaMA3微调全流程:从LoRA到QLoRA,7B参数模型推理速度提升4倍的代码实战
LLaMA3微调全流程:从LoRA到QLoRA,7B参数模型推理速度提升4倍的代码实战 发现了一个巨牛的人工智能学习网站,分享一下给大家!https://www.captainbed.cn/ccc 前言 在大模型时代,LLaMA系列作为开源社区的明星模型&#…...
日内组合策略思路
一、策略概述 本策略是一种针对日内交易设计的策略,其核心在于通过识别市场趋势和突破信号,结合动态止损和止盈机制,实现日内交易的盈利。策略以金字塔式的加仓方式控制风险,并通过灵活的平仓策略锁定收益。 二、交易逻辑思路 市场…...
从空气污染监测到嵌入式仿真教学:基于STM32与MQ135的实践探索
一、嵌入式系统在环境监测中的技术演进 随着全球城市化进程加速,世界卫生组织(WHO)数据显示,92%的人口长期暴露于超标PM2.5环境中。在此背景下,基于STM32微控制器的智能监测系统因其高性价比(单节点成本低…...
【数据结构】Map与Set结构详解
数据结构系列五:Map与Set(一) 一、接口的实现 1.方法上 2.成员上 二、Map的内外双接口结构 1.实现 1.1外部Map接口的实现 1.1.1临摹整体 1.1.2外部类实现整体 1.2内部Entry接口的实现 1.2.1临摹内部 1.2.2内部类实现内部 2.关系 3.意义 3.1逻辑内聚 …...
银河麒麟(内核CentOS8)安装rbenv、ruby2.6.5和rails5.2.6
一、安装 rbenv 和 ruby-build 1.安装 rbenv git clone https://github.com/rbenv/rbenv.git ~/.rbenv 2. 添加 rbenv 到 PATH echo export PATH"$HOME/.rbenv/bin:$PATH" >> ~/.bashrc echo eval "$(rbenv init -)" >> ~/.bashrc source ~…...
豆包桌面版 1.47.4 可做浏览器,免安装绿色版
自己动手升级更新办法: 下载新版本后安装,把 C:\Users\用户名\AppData\Local\Doubao\Application 文件夹的文件,拷贝替换 DoubaoPortable\App\Doubao 文件夹的文件,就升级成功了。 再把安装的豆包彻底卸载就可以。 桌面版比网页版…...
Linux 命令行与 vi/vim 编辑器完全指南
一、Linux 命令行基础 (一)命令与命令行简介 命令:Linux 系统内置的操作指令,以字符化形式使用,用于指示系统执行特定任务。 命令行(终端):提供字符化的操作界面,用户通…...
海量聊天消息处理:ShardingJDBC分库分表、ClickHouse冷热数据分离、ES复合查询方案、Flink实时计算与SpringCloud集成
海量聊天消息处理:ShardingJDBC分库分表、ClickHouse冷热数据分离、ES复合查询方案、Flink实时计算与SpringCloud集成 一、背景介绍 每天有2000万条聊天消息,一年下来几千万亿海量数据。为应对这种规模的数据存储和处理需求,本文将从以下几…...