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

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

相关文章

MyBatis-plus处理存储json数据过程

《MyBatis-plus处理存储json数据过程》文章介绍MyBatis-Plus3.4.21处理对象与集合的差异:对象可用内置Handler配合autoResultMap,集合需自定义处理器继承F... 目录1、如果是对象2、如果需要转换的是List集合总结对象和集合分两种情况处理,目前我用的MP的版本

Java中如何正确的停掉线程

《Java中如何正确的停掉线程》Java通过interrupt()通知线程停止而非强制,确保线程自主处理中断,避免数据损坏,线程池的shutdown()等待任务完成,shutdownNow()强制中断... 目录为什么不强制停止为什么 Java 不提供强制停止线程的能力呢?如何用interrupt停止线程s

Web服务器-Nginx-高并发问题

《Web服务器-Nginx-高并发问题》Nginx通过事件驱动、I/O多路复用和异步非阻塞技术高效处理高并发,结合动静分离和限流策略,提升性能与稳定性... 目录前言一、架构1. 原生多进程架构2. 事件驱动模型3. IO多路复用4. 异步非阻塞 I/O5. Nginx高并发配置实战二、动静分离1. 职责2

Python自动化处理PDF文档的操作完整指南

《Python自动化处理PDF文档的操作完整指南》在办公自动化中,PDF文档处理是一项常见需求,本文将介绍如何使用Python实现PDF文档的自动化处理,感兴趣的小伙伴可以跟随小编一起学习一下... 目录使用pymupdf读写PDF文件基本概念安装pymupdf提取文本内容提取图像添加水印使用pdfplum

C# LiteDB处理时间序列数据的高性能解决方案

《C#LiteDB处理时间序列数据的高性能解决方案》LiteDB作为.NET生态下的轻量级嵌入式NoSQL数据库,一直是时间序列处理的优选方案,本文将为大家大家简单介绍一下LiteDB处理时间序列数... 目录为什么选择LiteDB处理时间序列数据第一章:LiteDB时间序列数据模型设计1.1 核心设计原则

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

基于Redis自动过期的流处理暂停机制

《基于Redis自动过期的流处理暂停机制》基于Redis自动过期的流处理暂停机制是一种高效、可靠且易于实现的解决方案,防止延时过大的数据影响实时处理自动恢复处理,以避免积压的数据影响实时性,下面就来详... 目录核心思路代码实现1. 初始化Redis连接和键前缀2. 接收数据时检查暂停状态3. 检测到延时过

python 线程池顺序执行的方法实现

《python线程池顺序执行的方法实现》在Python中,线程池默认是并发执行任务的,但若需要实现任务的顺序执行,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋... 目录方案一:强制单线程(伪顺序执行)方案二:按提交顺序获取结果方案三:任务间依赖控制方案四:队列顺序消

Java利用@SneakyThrows注解提升异常处理效率详解

《Java利用@SneakyThrows注解提升异常处理效率详解》这篇文章将深度剖析@SneakyThrows的原理,用法,适用场景以及隐藏的陷阱,看看它如何让Java异常处理效率飙升50%,感兴趣的... 目录前言一、检查型异常的“诅咒”:为什么Java开发者讨厌它1.1 检查型异常的痛点1.2 为什么说

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优