一些基础概念

计算机处理某些任务需要时间,我们希望在等待时能做点别的事(并发)。操作系统提供了一种基础并发方式,但程序员可以做得更精细、更好。

1. 长时间任务很常见,干等着太无聊

  • 例子1:导出视频(计算密集型/CPU密集型)
    • 想象你用电脑剪辑家庭聚会视频。最后"导出"按钮一点,电脑就呼呼响,风扇狂转,整个电脑都变慢了甚至卡住。
    • 为什么? 导出视频需要电脑的"大脑"(CPU)和"图形处理器"(GPU)使出吃奶的劲儿处理每一帧画面。这非常耗费计算能力。
    • 问题: 如果电脑只有一个"大脑核心"(单核CPU),并且操作系统傻乎乎地让导出任务一直霸占着这个核心直到完成,那你在这期间啥也干不了!不能上网,不能听歌,不能打字。体验极差!
  • 例子2:下载文件(I/O密集型)
    • 想象你让电脑从网盘下载一个共享的家庭聚会大视频。
    • 过程: 电脑说:"喂,网络,给我那个文件!" 然后它就...等着。网络一点点把数据传过来。数据到了,电脑还要花时间把这些数据从临时地方搬到它该在的位置(比如你的硬盘)。虽然可能就几秒,但对每秒能做几十亿次计算的CPU来说,这几秒简直像几个世纪那么漫长!
    • 问题: CPU大部分时间在干等!它明明可以趁等网络数据的时候去干点别的活(比如让你看看邮件),但传统的下载方式会让CPU傻等。

2. 操作系统的"救场":基础并发(中断)

  • 操作系统怎么解决"导出视频卡死电脑"问题?
    • 操作系统很聪明,它不会让一个程序(比如视频导出)永远霸占CPU。它会频繁地、快速地打断正在运行的程序(比如视频导出),哪怕只打断一瞬间。
    • 效果: 在这一瞬间被打断时,操作系统赶紧让CPU去处理一下别的程序(比如你正在打的字,或者播放的音乐)。然后再切回视频导出程序继续算一点。如此反复。
    • 感觉: 对你用户来说,电脑看起来是"同时"在做几件事(导出视频+让你打字/听歌),虽然CPU核心在同一时刻其实只做一件事。这就叫并发 。操作系统通过中断实现了这种并发。
  • 操作系统怎么解决"下载文件时CPU干等"问题?
    • 同样用中断 !当下载程序发出"要数据"的请求后,在等待网络响应的漫长(对CPU而言)时间里,操作系统会打断这个等待中的下载程序。
    • 效果: CPU立刻被解放出来,去运行其他准备好运行的程序(比如刷新你的浏览器页面)。等网络数据终于到了,操作系统会再让下载程序继续运行一小会儿去处理数据,然后可能又被打断。如此反复。
    • 感觉: 下载在后台默默进行,你前台还能流畅地做其他事。

3. 程序员视角:操作系统并发还不够细,我们需要更好方式

  • 操作系统的并发是"粗粒度"的:
    • 它主要在不同程序(进程) 之间切换。比如在视频导出程序和浏览器程序之间切换。
  • 程序员需要"细粒度"并发:
    • 假设你写了一个下载管理器程序:
      • 希望用户点"开始下载"按钮后,整个程序界面就卡死、无响应,直到下载完。这体验很糟。
      • 用户可能想同时开始多个下载
    • 问题根源:传统API是"阻塞"的
      • 大部分用来读写文件、访问网络的函数(API),默认是阻塞式的。
      • "阻塞"是什么意思? 想象你打电话给客服查快递,客服说"请稍等,我帮您查"。然后电话里就安静了,你只能拿着电话干等,不能挂断去做饭,直到客服查完回来告诉你结果。这就是"阻塞"------这个电话(函数调用)卡住了你(程序),让你啥也干不了,直到它完成。
      • 在下载管理器中: 如果下载文件的函数是阻塞的,那么主线程(负责显示界面、响应用户点击)一旦调用这个下载函数,整个界面就会卡住,直到下载完成。
  • 传统解决方案:用线程(有代价)
    • 一种办法是为每个下载任务单独开一个线程 。想象线程就像公司里的不同员工。
      • 主线程(前台接待员):专门负责显示界面、响应用户操作(点按钮)。
      • 下载线程1(员工A):专门负责下载第一个文件。
      • 下载线程2(员工B):专门负责下载第二个文件。
    • 好处: 主线程(接待员)不会被下载任务卡住,用户界面依然流畅。多个下载可以同时进行(员工A和B各忙各的)。
    • 坏处: 创建和管理很多线程本身也需要消耗电脑资源(CPU时间、内存)。想象一下,如果用户一下子要下载1000个文件,你就得雇1000个临时工(线程),光是管理这1000个员工的开销就很大(线程开销),电脑可能反而变慢了。
  • 理想解决方案:非阻塞API + 异步编程
    • 非阻塞API: 想象打电话给一个智能客服。你说"帮我查快递",智能客服说"好的,查到了我会回拨你,你先去忙吧!",然后主动挂断了电话。这样你就可以立刻挂电话去做饭了!这就是"非阻塞"------你(主线程)没有被卡住,可以立刻去做别的事。
    • 异步编程(配合非阻塞API): 光有非阻塞API还不够,我们需要一种方便的编程方式来利用它。
      • 我们希望能像写普通的、顺序执行的代码(阻塞风格)那样简单明了,但实际效果是非阻塞的。
      • 目标代码: let data = fetch_data_from(url).await; println!("{data}");
        • fetch_data_from(url):调用非阻塞API开始下载。
        • .await:这个关键字是魔法!它的意思是:"我知道下载需要时间,我现在要去干点别的(比如刷新界面、处理其他下载请求)。当下载任务真的完成时,请回到这里 ,把下载好的数据赋值给data,然后继续执行下一行println!。"
      • 过程:
        1. 主线程执行到fetch_data_from(url).await;,启动非阻塞下载。
        2. .await告诉程序:"下载开始了,但我不会傻等。我现在暂停 执行这个函数的后续部分(println!),交出控制权,让主线程可以去处理其他事情(比如响应用户点击'暂停'按钮)。"
        3. 下载任务在后台(可能由操作系统或底层库管理)默默进行。
        4. 当下载完成时,一个"完成信号"会发出。
        5. 主线程在合适的时机(比如处理完一个用户点击后)检查到下载完成了,它就回到之前暂停的地方 ,把下载好的数据放进data,然后继续执行println!("{data}");
      • 感觉: 代码看起来是顺序写的("开始下载" -> "打印数据"),但实际执行时,在await点发生了"暂停并切换",等下载完成再"回来继续"。程序(主线程)在等待期间没有被阻塞,可以高效地做其他工作。

总结关键概念

  1. CPU密集型 (Compute-bound): 任务主要消耗CPU/GPU的计算能力(如视频转码、复杂计算)。快慢取决于CPU/GPU的速度。
  2. I/O密集型 (IO-bound): 任务主要消耗在等待输入/输出上(如网络下载、读写硬盘、等待数据库响应)。快慢取决于外部设备(网络、磁盘)的速度。
  3. 阻塞 (Blocking): 调用一个函数时,程序必须停下所有事情,一直等到这个函数完全执行完毕并返回结果,才能继续往下走。像打电话干等客服查快递。
  4. 非阻塞 (Non-blocking): 调用一个函数时,函数立刻返回 (可能返回一个"还没完成"的信号),程序不用干等,可以立刻去做其他事情。像打电话让智能客服查到后回拨。
  5. 并发 (Concurrency): 让一个系统(一台电脑、一个程序)看起来能同时处理多个任务。实际可能是在多个任务间快速切换(单核CPU),也可能是真并行(多核CPU)。
  6. 操作系统中断: 操作系统强制暂停当前运行的程序,让CPU去运行其他程序的基础并发机制。主要解决程序间的资源争抢。
  7. 线程: 程序内部的执行分支。可以用于实现并发,但创建和管理大量线程开销大。
  8. 异步编程 (Async/Await):
    • 一种编写非阻塞代码的编程模式。
    • 使用async标记的函数可以在内部使用await
    • await 点:程序执行到这里,如果等待的操作(如网络请求)还没完成,就暂停 这个函数的执行,交出控制权 让程序去做其他事。当等待的操作完成时,程序会回来从这里继续执行。
    • 目标: 用看起来像顺序执行(阻塞风格)的简洁代码,实现高效的、非阻塞的并发操作(尤其是处理大量I/O密集型任务),避免大量线程的开销。

简单来说: 为了避免电脑在干重活(计算)或者等人送东西(I/O)时我们只能傻等,操作系统会自己切换任务来让我们能同时干几件事(基础并发)。但程序员写程序时(比如下载工具),需要更精细的控制,让程序内部也能"同时"做多个任务且不卡界面。传统用"多开员工"(多线程)的方式管太多人很累。更好的办法是用"智能客服+任务暂停/继续"的方式(非阻塞API + 异步编程),这样代码写起来简单直接(像.await),效率又高,特别适合处理大量需要等待的任务(如下载、网络请求)。

相关推荐
该用户已不存在16 小时前
OpenAI 用 Rust 重写 AI 工具,你的本地开发环境跟上了吗?
前端·javascript·rust
火柴就是我16 小时前
Rust 学习只 字符串操作知识点
rust
寻月隐君19 小时前
Rust + Protobuf:从零打造高效键值存储项目
后端·rust·github
维维酱20 小时前
Rust - Send & Sync
rust
Kapaseker1 天前
Android程序员初学Rust-通道
后端·rust
我是前端小学生1 天前
如何在macos上安装rust开发环境
rust
UestcXiye1 天前
Rust 学习笔记:关于共享状态并发的练习题
rust
gregmankiw2 天前
C#调用Rust动态链接库DLL的案例
开发语言·rust·c#