浅谈进程,线程,协程以及服务端高并发的处理

2024-08-23 02:04

本文主要是介绍浅谈进程,线程,协程以及服务端高并发的处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

进程、线程、协程

  • 进程:独立的程序实例,资源开销较大,适合隔离性要求高的任务。

    • 独立性:进程具有独立的内存空间和资源,互不干扰。

    • 资源开销大:由于每个进程都需要分配独立的内存和资源,创建和切换进程的开销相对较大。

    • 进程间通信复杂:进程之间的通信通常需要通过操作系统提供的机制,如管道、消息队列或共享内存

  • 线程:进程中的执行单元,轻量级,适合需要共享资源的并发任务。

    • 共享内存空间:同一进程中的所有线程共享相同的内存地址空间,因此线程之间的通信比进程之间更容易。

    • 轻量级:线程创建和切换的开销比进程小,因为线程间共享资源,不需要像进程那样进行大量的上下文切换。

    • 并发执行:在多核处理器上,不同线程可以真正实现并行运行。

  • 协程:比线程更轻量的并发执行单元,适合 I/O 密集型任务和对性能要求较高的场景。

    • 轻量级:协程比线程更轻量,通常不需要像线程那样的上下文切换开销,因为它们在用户态完成调度。

    • 非抢占式多任务:协程的切换是显式的,只有当协程主动让出执行权时,才会切换到其他协程。这使得协程之间的并发执行更加可控。

    • 无需多线程环境:协程在单线程环境中就能实现高效的并发,因此非常适合 I/O 密集型操作,如网络请求或文件读取。

进程出现的目的,是为了更好的利用CPU资源。

例如:

假设有两个任务A和B,当A遇到IO操作,CPU默默地等待任务A读取完操作再去执行任务B,这样无疑是对CPU资源的极大的浪费。若在任务A读取数据时,让任务B执行,当任务A读取完数据后,再切换到任务A执行,这样就可以更好地利用CPU资源。这里的切换涉及到状态的保存,状态的恢复,需要有一个东西去记录任务A和任务B分别需要什么资源,怎样去识别任务A和任务B,这时进程就出现了。

因此,通过进程来分配系统资源,标识任务。

如何分配CPU去执行进程称之为调度,进程状态的记录,恢复,上下文切换(简称切换)。

其次,若上面提及的任务A是一个文本程序,需要接受键盘输入,将内容显示在屏幕上,还需要保存信息到硬盘中。

若只有一个进程,会造成同一时间只能干一样事的尴尬(当保存时,就不能通过键盘输入内容)。若有多个进程,每个进程负责一个任务,进程A负责接收键盘输入的任务,进程B负责将内容显示在屏幕上的任务,进程C负责保存内容到硬盘中的任务。这里进程A,B,C间的协作涉及到了进程通信问题,而且有共同都需要拥有的东西-------文本内容,不停地切换造成性能上的损失。若有一种机制,可以使任务A,B,C共享资源,这样上下文切换所需要保存和恢复的内容就少了,同时又可以减少通信所带来的性能损耗,那就好了。这时线程出现了。

线程共享进程的大部分资源,并参与CPU的调度。

当操作系统切换线程时,需要保存当前线程的状态(如寄存器、程序计数器、堆栈指针等),然后加载下一个线程的状态,这个过程被称为上下文切换。线程上下文切换通常涉及系统调用和内核参与,因为线程通常由操作系统内核来管理。

假设当涉及到大规模的并发请求连接时,例如有一万个人同时连接我的服务器,但系统资源有限,如果以线程作为处理单元,调内部系统资源的话大部分线程都处于等待状态

传统的请求是线程池+一个请求创建一个线程 (java应用服务器tomcat,Jetty) 由于是这种方式有很大的并发限制所以有了如下的优化:

  • NIO 提供了非阻塞的 I/O 操作,这使得线程不需要等待 I/O 操作完成,而是可以在 I/O 准备好时被通知。提高了线程利用率。由于一个线程可以管理多个通道,NIO 在高并发场景下能显著减少线程数量,从而提高性能。

  • 线程池,通常使用线程池管理线程。线程池限制了同时运行的线程数,并通过复用线程来减少资源消耗。

局限性:

  • 资源开销:每个线程都占用一定的系统资源,包括内存、CPU 时间、操作系统调度等。当有大量线程(如一万个线程)同时运行时,系统的内存和 CPU 资源很快就会被耗尽。

  • 上下文切换:当多个线程在 CPU 上并发运行时,操作系统内核需要频繁地进行上下文切换,保存和恢复每个线程的状态。这种上下文切换是有开销的,尤其是在大量线程之间频繁切换时,会导致系统性能下降,很多时间可能都浪费在切换上下文上,而不是实际处理任务。

协程通过在线程中实现调度,避免了陷入内核级别的上下文切换造成的性能损失,进而突破了线程在IO上的性能瓶颈。

使用协程就可以实现线程自己调度,不陷入内核级别的上下文切换。协程可以在 I/O 操作时主动让出 CPU,线程不会被阻塞,其他协程可以继续执行。这样,单个线程可以管理多个协程,从而极大地提高了并发处理能力,充分利用 CPU 资源。

协程切换是在用户态完成的,不涉及系统调用。协程之间的切换通常只需要保存和恢复少量的状态信息,切换开销极小,因此在高并发场景下,协程的性能优势非常明显。

为什么协程不需要经过内核级别的上下文切换,我是这样认为的:

内核态 vs. 用户态:操作系统运行在两种模式下:内核态和用户态。内核态拥有对硬件和系统资源的完全控制,用户态则是普通应用程序运行的状态。线程切换通常需要切换到内核态进行调度,协程的调度和切换不需要通过系统调用来进行,完全在用户态操作。这意味着协程切换不会触发内核级别的上下文切换,不涉及操作系统的调度器,也不需要保存和恢复操作系统管理的线程栈和寄存器状态等。

进程和线程都是操作系统自带的,协程是有些程序原生支持的,例如go,lua, 有些是后期版本才有的,比如python2.5 以后

Python:Python 的 asyncio 库是一个常用的协程框架,可以轻松创建和管理协程。使用 asyncio,一个线程可以处理很多的并发任务,例如网络 I/O、数据库查询等。

JavaScript:JavaScript 的异步操作(如 Promise 和 async/await)允许在单线程环境中实现协程式的并发处理。Node.js 就利用了这种模型,允许在单线程中处理大量并发 I/O 操作。

Python 协程的举例:

异步IO操作:

import asyncio
import aiohttpasync def download_file(session, url):async with session.get(url) as response:content = await response.read()filename = url.split('/')[-1]with open(filename, 'wb') as f:f.write(content)print(f"Downloaded {filename}")async def main():urls = ['http://example.com/file1','http://example.com/file2','http://example.com/file3']async with aiohttp.ClientSession() as session:tasks = [download_file(session, url) for url in urls]await asyncio.gather(*tasks)asyncio.run(main())

并发任务的协调与管理

import asyncioasync def task(name, delay):await asyncio.sleep(delay)print(f"Task {name} completed.")return nameasync def main():# A,B并发执行 两个协程task_a = asyncio.create_task(task("A", 3))task_b = asyncio.create_task(task("B", 2))await asyncio.gather(task_a, task_b)# Task C 在A,B后执行 C 可能依赖AB 的结果await task("C", 1)asyncio.run(main())

await 关键字用于暂停协程的执行,直到 await 后面的协程对象完成。它使得协程可以异步地等待其他协程的结果,而不会阻塞事件循环。

服务端高并发处理

对于服务端提高并发常见的处理方案

常见的系统架构图

1. 负载均衡

  • 水平扩展(Scale-out):通过增加服务器数量,使用负载均衡器(如Nginx)将请求分发到多台服务器上,从而分散压力。负载均衡器可以基于不同的策略(如轮询、最小连接数、IP哈希等)将流量分配给各个后端服务器。

2. 缓存技术

  • 数据缓存:使用缓存机制(如Redis、Memcached)缓存热点数据,减少数据库的查询次数。常见的缓存策略有本地缓存、分布式缓存等。

  • 页面缓存:对于动态生成的页面,尤其是变化不频繁的页面,可以将其缓存一段时间,从而减少服务器的计算负载。

3. 数据库优化

  • 读写分离:通过主从复制,将读操作分配到从数据库,写操作由主数据库处理,从而减轻主数据库的负载。

  • 分库分表:当单一数据库实例无法承载大量数据时,可以将数据按某种规则拆分到多个数据库(分库)或多个表(分表)中。

  • 索引优化:合理使用数据库索引,加速查询速度,同时避免过多的索引导致的写操作性能下降。

  • 使用NoSQL数据库:在某些场景下(如处理海量数据、高频写入等),可以使用NoSQL数据库(如MongoDB、Cassandra、HBase等)替代传统的关系型数据库。

4. 异步处理与消息队列

  • 异步处理:将非实时任务(如日志记录、通知发送、复杂计算)通过异步方式处理,减少主线程的负载。可以使用事件驱动或基于回调的异步编程模型(如Node.js、Python的异步IO)。

  • 消息队列:使用消息队列(如RabbitMQ、Kafka、ActiveMQ)解耦各个系统组件,将需要排队的任务放入队列中,异步处理,从而避免系统过载。

5. 服务拆分与微服务架构

  • 服务拆分:将单一的大型应用程序拆分为多个独立的服务,减少耦合,增强系统的可扩展性。每个服务可以独立扩展并优化,从而提高系统整体的并发处理能力。

  • 微服务架构:采用微服务架构,将不同的业务逻辑分离成独立的微服务,通过RESTful API或消息队列通信,增强系统的灵活性和伸缩性。

6. 限流与降级

  • 限流:对接口或服务设置访问频率限制,防止因瞬时流量过大导致系统崩溃。可以采用令牌桶算法、漏桶算法等限流策略。

  • 熔断与降级:当系统部分组件过载或不可用时,及时熔断(停止调用)该组件,并执行降级策略(如返回默认值、提供备用服务),以保证核心功能的正常运行。

7. 操作系统与Web服务器调优

  • 操作系统调优:调整操作系统的网络参数(如文件描述符上限、TCP连接数等),优化内存管理,减少上下文切换,提升并发处理能力。

  • Web服务器调优:调整Web服务器(如Nginx、Apache、Tomcat等)的并发连接数、线程池大小、缓存配置等参数,以适应高并发环境。

8. 前端优化

  • 减少请求数 减少服务器压力。

  • 延迟加载:使用Lazy Load技术,推迟加载非关键资源,以减少页面加载时的并发请求数。

这篇关于浅谈进程,线程,协程以及服务端高并发的处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1098036

相关文章

java如何实现高并发场景下三级缓存的数据一致性

《java如何实现高并发场景下三级缓存的数据一致性》这篇文章主要为大家详细介绍了java如何实现高并发场景下三级缓存的数据一致性,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 下面代码是一个使用Java和Redisson实现的三级缓存服务,主要功能包括:1.缓存结构:本地缓存:使

SpringBoot 异常处理/自定义格式校验的问题实例详解

《SpringBoot异常处理/自定义格式校验的问题实例详解》文章探讨SpringBoot中自定义注解校验问题,区分参数级与类级约束触发的异常类型,建议通过@RestControllerAdvice... 目录1. 问题简要描述2. 异常触发1) 参数级别约束2) 类级别约束3. 异常处理1) 字段级别约束

Java中的xxl-job调度器线程池工作机制

《Java中的xxl-job调度器线程池工作机制》xxl-job通过快慢线程池分离短时与长时任务,动态降级超时任务至慢池,结合异步触发和资源隔离机制,提升高频调度的性能与稳定性,支撑高并发场景下的可靠... 目录⚙️ 一、调度器线程池的核心设计 二、线程池的工作流程 三、线程池配置参数与优化 四、总结:线程

WinForm跨线程访问UI及UI卡死的解决方案

《WinForm跨线程访问UI及UI卡死的解决方案》在WinForm开发过程中,跨线程访问UI控件和界面卡死是常见的技术难题,由于Windows窗体应用程序的UI控件默认只能在主线程(UI线程)上操作... 目录前言正文案例1:直接线程操作(无UI访问)案例2:BeginInvoke访问UI(错误用法)案例

一文解密Python进行监控进程的黑科技

《一文解密Python进行监控进程的黑科技》在计算机系统管理和应用性能优化中,监控进程的CPU、内存和IO使用率是非常重要的任务,下面我们就来讲讲如何Python写一个简单使用的监控进程的工具吧... 目录准备工作监控CPU使用率监控内存使用率监控IO使用率小工具代码整合在计算机系统管理和应用性能优化中,监

Java堆转储文件之1.6G大文件处理完整指南

《Java堆转储文件之1.6G大文件处理完整指南》堆转储文件是优化、分析内存消耗的重要工具,:本文主要介绍Java堆转储文件之1.6G大文件处理的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言文件为什么这么大?如何处理这个文件?分析文件内容(推荐)删除文件(如果不需要)查看错误来源如何避

使用Python构建一个高效的日志处理系统

《使用Python构建一个高效的日志处理系统》这篇文章主要为大家详细讲解了如何使用Python开发一个专业的日志分析工具,能够自动化处理、分析和可视化各类日志文件,大幅提升运维效率,需要的可以了解下... 目录环境准备工具功能概述完整代码实现代码深度解析1. 类设计与初始化2. 日志解析核心逻辑3. 文件处

Java docx4j高效处理Word文档的实战指南

《Javadocx4j高效处理Word文档的实战指南》对于需要在Java应用程序中生成、修改或处理Word文档的开发者来说,docx4j是一个强大而专业的选择,下面我们就来看看docx4j的具体使用... 目录引言一、环境准备与基础配置1.1 Maven依赖配置1.2 初始化测试类二、增强版文档操作示例2.

MyBatis-Plus通用中等、大量数据分批查询和处理方法

《MyBatis-Plus通用中等、大量数据分批查询和处理方法》文章介绍MyBatis-Plus分页查询处理,通过函数式接口与Lambda表达式实现通用逻辑,方法抽象但功能强大,建议扩展分批处理及流式... 目录函数式接口获取分页数据接口数据处理接口通用逻辑工具类使用方法简单查询自定义查询方法总结函数式接口

Linux进程CPU绑定优化与实践过程

《Linux进程CPU绑定优化与实践过程》Linux支持进程绑定至特定CPU核心,通过sched_setaffinity系统调用和taskset工具实现,优化缓存效率与上下文切换,提升多核计算性能,适... 目录1. 多核处理器及并行计算概念1.1 多核处理器架构概述1.2 并行计算的含义及重要性1.3 并