32. 线程、进程与协程

embedded/2024/12/24 11:14:41/

一、什么是多任务

  如果一个操作系统上同时运行了多个程序,那么称这个操作系统就是 多任务的操作系统,例如:Windows、Mac、Android、IOS、Harmony 等。如果是一个程序,它可以同时执行多个事情,那么就称为 多任务的程序

  一个 CPU 默认可以执行一个程序,如果想要多个程序一起执行,理论上就需要多个 CPU 来执行。

  如果一个 CPU 是一个核心,理论上只能同时运行一个任务,但是事实上却可以运行很多个任务。这是因为操作系统控制着 CPU,让 CPU 做了一个特殊的事情,一会运行一个任务,然后快速的运行另一个任务,依次类推,实现了多个任务,看上去 “同时” 运行多个任务。

并发:是一个对假的多任务的描述;

并行:是真的多任务的描述;

二、进程与线程

  计算机程序只是存储在磁盘上的可执行二进制(或其它类型)文件。只有把它们加载到内存中从被操作系统调用,才拥有其生命期。

  进程(process)则是一个执行中的程序。每个进程都拥有自己的地址空间、内存、数据栈以及其它用于跟踪执行的辅助数据。操作系统管理其上所有进程的执行,并为这些进程合理分配时间。进程也可以通过派生新的进程来执行其它任务,不过因为每个新进程也都拥有自己的内存和数据栈等,所以只能采用进程间通信的方式共享数据;

  线程(thread)与进程类似,不过它们是同一个进程下执行的,并共享相同的下上文。线程包括开始、执行顺序和结束三部分。它有一个指令指针,用于记录当前运行的上下文。当其它线程运行时,它可以被抢占(中断)和临时挂起(也称为睡眠)—— 这种做法叫做让步(yielding)。

  一个进程中的各个线程与主线程共享同一片数据空间。线程一般是以并发方式执行的。在单核 CPU 系统中,因为真正的并发是不可能的,所以线程的执行实际上是这样规划的:每个线程运行一小会,然后让步给其它线程(再次排队等待更多的 CPU 时间)。在整个进程的执行过程中,每个线程执行它自己特定的任务,在必要时和其它线程进行结果通信。

  但是这种共享数据也是存在风险的。如果两个或多个线程访问同一片数据,由于数据访问顺序不同,可能导致结果不一致。这种情况通常称为 “竞态条件”(race condition)。另一个需要注意的问题时,线程无法给予公平的执行时间。这是因为一些函数会在完成前保持阻塞状态,如果没有专门为多线程情况进行修改,会导致 CPU 的时间分配向这些贪婪的函数倾斜。

  在实现多任务时,线程切换从系统层面远不止保存和恢复 CPU 上下文这么简单。操作系统为了程序运行的高效性,每个线程都有自己缓存 Cache 等数据。操作系统还会帮你做这些数据的恢复操作。所以线程的切换比较耗性能。但是协程的切换只是单纯的操作 CPU 的上下文。

线程是计算机中可以被 CPU 调度的最小单元,进程是计算机资源分配的最小单元;进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域;

一个程序,至少有一个进程,一个进程中至少有一个线程,最终是线程在工作;

一个进程内可以开设多个线程,在用一个进程内开设多个线程无需再次申请空间及拷贝代码的操作,开设线程的开销远远的要小于进程的开销;

单核 CPU,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务。但是因为 CPU 时间单元特别短,因此感觉不出来;

三、多进程的使用场景

  多进程 适合 计算密集型 的场景。

【1】、多进程的使用

python">import os, timefrom multiprocessing import Processdef task():val = 1for i in range(1, 100000):val *= iif __name__ == "__main__":l = []count = int(os.cpu_count())print(f"当前计算机CPU核心个数:{count}")start_time = time.time()for i in range(count):p = Process(target=task)p.start()l.append(p)for p in l:p.join()print(f"运行时间:{time.time() - start_time}")

【2】、多线程的使用

python">import os, timefrom threading import Threaddef task():val = 1for i in range(1, 100000):val *= iif __name__ == "__main__":l = []count = int(os.cpu_count())print(f"当前计算机CPU核心个数:{count}")start_time = time.time()for i in range(count):t = Thread(target=task)t.start()l.append(t)for t in l:t.join()print(f"运行时间:{time.time() - start_time}")

四、多线程的使用场景

  多线程 适合 IO 密集型 场景

【1】、多进程的使用

python">import timefrom multiprocessing import Processdef task():time.sleep(3)if __name__ == "__main__":l = []start_time = time.time()for i in range(1000):p = Process(target=task)p.start()l.append(p)for p in l:p.join()print(f"运行时间:{time.time() - start_time}")

【2】、多线程的使用

python">import timefrom threading import Threaddef task():time.sleep(3)if __name__ == "__main__":l = []start_time = time.time()for i in range(1000):t = Thread(target=task)t.start()l.append(t)for t in l:t.join()print(f"运行时间:{time.time() - start_time}")

http://www.ppmy.cn/embedded/148315.html

相关文章

浏览器工作原理与实践-12|栈空间和堆空间:数据是如何存储的

12|栈空间和堆控件:数据是如何存储的 JavaScript内存机制 function foo(){var a 1var b aa 2console.log(a) // 2console.log(b) // 2 } foo()function foo(){var a {name:"极客时间"}var b aa.name "极客邦"console.log(a) // {name:&…

leetcode17:电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 示例 1: 输入:digits "23" 输出&#…

JDK11下载安装和配置超详细过程

一、下载JDK11资源包 JDK11安装包文件夹资源资源https://download.csdn.net/download/Z0412_J0103/90160803 二、配置环境 2.1 找到文件位置 2.2 打开文件,点击“下一步” ” 2.3 记住文件配置路径,并不要更改,点击下一步 2.4 等待安装完…

【Prometheus】【实战篇(七)】在 Grafana 中配置数据源并使用 Prometheus Node Exporter

目录 1、配置 Prometheus 数据源2、导入 Node Exporter 仪表盘3、查看Dashbords监控数据4、通过JSON文件离线方式导入仪表盘4.1、访问grafana的官网下载配置文件4.2、将对应的文件内容填充到如下 5、总结 1、配置 Prometheus 数据源 登录到 Grafana 打开浏览器,访问…

express+mysql实现注册功能

这里写自定义目录标题 app.jsregister.htmlsuccess.html初始化项目mysql app.js const express require("express"); const bodyParser require("body-parser"); const mysql require("mysql"); const path require("path"); con…

Redis 多实例配置说明

Redis 多实例就是在在单个服务器上运行多个redis服务(通过不同的配置文件来启动不同的redis进程)。 所以需要保证以下配置项在当前服务器上唯一。 监听端口: port PID文件: pidfile 日志文件 :logfile 持久化数据的…

【Linux】ubuntu通过远程命令行启动桌面应用

背景介绍 我有一台服务器, 但没有外接鼠标和键盘。 安装了驱动和桌面程序, 连接到了显示器大屏。 配置了固定的ip地址和ssh服务。 目的 想在服务器上开启浏览器显示在大屏上。 如何通过ssh命令来实现这呢 具体代码 export DISPLAY:0.0 …

数据结构_平衡二叉树

结点类 构造函数分为有参和无参,相同点都是初始化树高为1 class Node { public:int data; // 用于输出int val; // 数据域,用于排序int height; // 树高Node* left;Node* right;Node();Node(int v, int d);static int max(int a, int b); };Node::N…