首页 > 编程语言 >从线程到协程:理解现代编程中的并发革命(一)

从线程到协程:理解现代编程中的并发革命(一)

时间:2025-01-15 18:57:44浏览次数:3  
标签:std 异步 协程 编程 并发 线程

在现代软件开发中,性能与效率始终是工程师追求的目标,而并发编程正是实现这一目标的关键手段。从传统的线程模型到轻量级的协程技术,编程范式正经历一场深刻的变革。线程为我们带来了并发的能力,但伴随而来的是高昂的资源成本和复杂的管理难度。而协程的出现,则为开发者提供了一种更加轻量、高效且易于维护的解决方案,它重新定义了异步代码的书写方式,并极大提升了高并发场景下的性能与可扩展性。在这篇文章中,我们将深入剖析协程为何成为并发编程的下一个里程碑,以及它与传统线程的关键区别和应用场景。

1. 协程是什么 

协程(Coroutine) 是一种比线程更轻量级的程序组件,允许程序在执行过程中暂停、挂起并在需要时恢复。协程通常用于简化异步编程和并发任务的实现。

协程的核心理念是:可以在函数中保存执行状态,并在需要时恢复执行,从而实现非阻塞的任务切换。

协程的特点

  1. 轻量级

    • 协程运行在用户态,不依赖操作系统的线程调度机制,切换开销低。
    • 同一个线程可以运行多个协程,内存占用远小于线程。
  2. 非抢占式

    • 协程的执行是协作的,它会主动挂起或让出 CPU,而不会像线程那样被操作系统抢占。
  3. 状态保存

    • 协程可以在暂停时保存当前的状态(如局部变量、调用栈等),下次恢复时继续执行。
  4. 易于控制

    • 协程的切换是由程序控制的,而非操作系统,因此开发者可以决定协程的调度时机。

协程与传统线程的对比

特性协程线程
调度方式用户态调度操作系统调度
内存占用非常小,通常只需几个 KB较大,栈通常需要几 MB
切换开销小,直接切换上下文高,需要保存和恢复线程上下文
并发能力单线程内实现并发多线程并发,需要操作系统支持
阻塞操作会挂起当前协程,不影响其他协程阻塞线程会影响整个线程池的效率

2. 为什么要有协程

 

1. 简化异步编程

在传统异步编程中,通常需要通过回调函数或状态机来管理异步任务,这会导致代码复杂且难以维护(如“回调地狱”)。

有了协程后:

  • 可以使用同步的编程风格编写异步代码。
  • 协程能够暂停并恢复执行,使得程序流程更加直观。
示例:传统异步 VS 协程

传统回调方式:

void fetchDataAsync(std::function<void(int)> callback) {
    // 模拟异步操作
    std::thread([callback]() {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        callback(42); // 返回结果
    }).detach();
}

 使用协程:

#include <coroutine>
#include <iostream>
#include <chrono>
#include <thread>

// 协程任务
std::future<int> fetchDataAsync() {
    co_await std::suspend_always{};
    co_return 42; // 返回结果
}

协程避免了嵌套回调,代码更清晰。

2. 更高效的并发

线程在操作系统中是重量级的,它们有固定的栈空间和上下文切换开销。而协程是用户态的实现,比线程更加轻量。

  • 线程
    • 每个线程占用较多内存(如栈内存)。
    • 线程的切换需要上下文切换,性能较低。
  • 协程
    • 协程仅在需要时占用少量内存。
    • 切换协程无需操作系统参与,性能开销极小。

3. 提高资源利用率

协程通过非阻塞的方式管理 I/O 等耗时操作,提高资源的利用效率。

问题:传统阻塞式模型的缺点

  • 如果一个线程等待某个 I/O 操作完成(如网络请求),该线程会被阻塞,无法处理其他任务。

协程的优势

  • 协程允许任务在 I/O 等待期间挂起,释放 CPU 给其他任务。I/O 完成后协程恢复执行,效率更高。
示例:

传统线程模型:

void blockingFunction() {
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 阻塞
    std::cout << "Task done" << std::endl;
}

协程模型:

std::future<void> nonBlockingFunction() {
    co_await std::suspend_always{}; // 协程挂起,释放 CPU
    std::cout << "Task done" << std::endl;
}

4. 更好的代码可读性

协程可以让异步代码看起来像同步代码,避免了复杂的状态管理和控制流操作,使代码更易读、更易维护。

示例:

在协程中,awaitco_await 等语法明确表达出暂停和恢复的意图,而不是通过复杂的回调来实现。

async function example() {
    let result = await fetchData(); // 挂起并等待结果
    console.log(result);
}

5. 应对高并发场景

协程在需要处理大量并发任务的场景中(如 Web 服务、游戏引擎)非常有用。

  • 线程模型: 每个任务使用一个线程,可能会耗尽系统资源。
  • 协程模型: 多个任务共享线程,每个任务在需要时挂起,不会阻塞其他任务。
示例:Web 服务器
  • 使用线程:每个请求使用一个线程,容易因线程数过多导致资源枯竭。
  • 使用协程:每个请求是一个协程,在等待 I/O 时挂起,显著减少资源消耗。

6. 可移植性和灵活性

协程是语言级的抽象,与底层线程或操作系统无关,可以跨平台实现。


7. 支持更多应用场景

协程能够很好地支持以下场景:

  1. 异步 I/O 操作(如文件读取、网络请求)。
  2. 游戏循环:需要频繁挂起和恢复任务。
  3. 分布式系统:在大规模并发任务中管理状态和任务切换。
  4. 协同多任务处理:多个任务以非抢占式方式交替执行。

3. 总结 

为什么要有协程? 协程的引入是为了简化异步编程和高并发任务的实现,它通过轻量级的用户态调度,降低了资源消耗和上下文切换的开销。相比传统线程,协程更高效,能够在需要时挂起并释放 CPU 资源,同时以同步的编程风格书写异步逻辑,使代码更直观、易读、易维护。尤其在处理异步 I/O、高并发任务、游戏开发和分布式系统时,协程能显著提升性能和开发效率,是现代编程应对复杂场景的重要工具。

标签:std,异步,协程,编程,并发,线程
From: https://blog.csdn.net/hz_uzi/article/details/145116073

相关文章

  • flask之 scoped实现线程安全.py
    1、用法导入模块,将Session传入scoped_session即可fromsqlalchemy.ormimportsessionmakerfromsqlalchemyimportcreate_enginefromsqlalchemy.ormimportscoped_sessionfrommodelsimportUsersfromthreadingimportlocalengine=create_engine("mysql+pymysql:......
  • 美团动态线程池
    使用线程池ThreadPoolExecutor过程中你是否有以下痛点呢?代码中创建了一个ThreadPoolExecutor,但是不知道那几个核心参数设置多少比较合适凭经验设置参数值,上线后发现需要调整,改代码重启服务,非常麻烦线程池相对开发人员来说是个黑盒,运行情况不能及时感知到,直到出现问题......
  • 线程每次iodelay监控及D状态开始和结束监控并做堆栈记录
    一、背景在之前的博客 获取进程或线程级别的iodelay的方法_io验证延时链-CSDN博客里,我们讲到了获取进程或线程的iodelay的方法,但是博客里讲到的获取iodelay的值是一个累积值,并不能准确的捕获到每个单次的iodelay具体是多少。这篇博客里是为了监控每个单次的iodelay,除了监控i......
  • Java多进程多线程处理详解
    在Java编程中,多进程和多线程是两种常见的并发编程技术,用于提高程序的执行效率和响应速度。本文将详细介绍Java中的多进程和多线程处理,包括理论概述和代码示例。通过本文,你将了解如何在Java中实现多进程和多线程,以及它们在实际应用中的价值和意义。一、理论概述1.多进程与多线程......
  • 可编程交流负载标准
    可编程交流负载标准是电力电子测试领域的重要组成部分,它为交流电源、变频器、逆变器等设备的测试提供了标准化的负载条件。这种可编程性使得测试更加灵活和精确,能够满足不同设备和应用场景的需求。核心在于其可编程性,这意味着用户可以根据自己的需求,通过编程来设定负载的各种参数......
  • 【IO编程】静态库 和 动态库
    在软件开发中,库是一组已编译的代码集合,提供了程序可以直接调用的功能模块(如数学运算、字符串处理、文件操作等)。库的主要作用是提高代码复用性、减少重复开发,并提供标准化功能。什么是库库(Library)是一个包含函数、类或其他可重用代码的集合。开发者在程序中调用库中的函......
  • 【程序猿面试真题——计算机基础知识和编程】如何寻找二次曲线(离散的点连成的)的最小值
    【程序猿面试真题——计算机基础知识和编程】如何寻找二次曲线(离散的点连成的)的最小值?【程序猿面试真题——计算机基础知识和编程】如何寻找二次曲线(离散的点连成的)的最小值?文章目录【程序猿面试真题——计算机基础知识和编程】如何寻找二次曲线(离散的点连成的)的最小......
  • Xbox:Xbox游戏编程入门_2024-07-19_20-02-54.Tex
    Xbox:Xbox游戏编程入门游戏开发环境搭建安装VisualStudio环境准备在开始Xbox游戏开发之前,首先需要一个强大的集成开发环境(IDE),VisualStudio是微软提供的一个广泛使用的开发工具,它支持多种编程语言,包括C++,这是Xbox游戏开发的主要语言。下载与安装访问VisualStudio官......
  • 编程小白对C语言的认识与看法
     对于一个刚刚进入大学数学类专业的学生,并且学校在大一还没有开展计算机相关课程,我对C  语言的了解仅仅是一个名词,一串代码,一门课程。当我真正在学习的时候,我才发现C语言并没有这么简单。 我问过许多C语言的朋友,C语言是什么?他们并不能很好的回答我的问题,对于他们而言,C......
  • 【花雕学编程】Arduino动手做(246)---ESP8266 NodeMCU V3 Web Server
    37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里准备逐一动手尝试系列实验,不管成功(程序走通)与否,都会记录下来——小小的......