32. 线程、进程与协程

一、什么是多任务

  如果一个操作系统上同时运行了多个程序,那么称这个操作系统就是 多任务的操作系统 ,例如: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, time

from multiprocessing import Process

def task():
    val = 1
    for i in range(1, 100000):
        val *= i


if __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, time

from threading import Thread

def task():
    val = 1
    for i in range(1, 100000):
        val *= i


if __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 time

from multiprocessing import Process

def 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 time

from threading import Thread

def 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}")
相关推荐
niucloud-admin1 小时前
JAVA V6 多商户商城 开发文档——job 计划任务开发
java·python·github
小叶肥辉1 小时前
LangChain链和LangGraph图的学习笔记【三】——分别用langchain_openai库和langchain_community库调用大模型
笔记·python·langchain
2401_873479402 小时前
IP属地为什么有时显示外省?用IP查询工具核查动态分配、运营商出口与GeoIP库
python·tcp/ip·ip
OKkankan4 小时前
LangChain 能力详解!:输出解析、RAG、向量数据库与 Retriever 检索器
数据结构·python·langchain·ai应用
山哥ol5 小时前
【Geany 环境配置与中文乱码解决参考】
python
会飞锦鲤5 小时前
基于 Mask R-CNN 的药片缺陷检测系统
人工智能·pytorch·python·神经网络·resnet-50
默 语5 小时前
Java新手入门:从零开始安装JDK并配置环境变量
java·开发语言·python·mysql·group by·1024程序员节·数据去重
清水白石0086 小时前
Python 异步编程深度解析:Cancellation 到底是异常还是控制信号?从 asyncio 取消机制到企业级事务设计最佳实践
开发语言·python
Patrick在香港6 小时前
Python 审计香港开放数据目录:两个端点差 10 倍,只有 9.3% 的资源标了「最后修改时间」
开发语言·数据库·python·数据分析·api·数据治理·开放数据
IT毕设梦工厂6 小时前
计算机毕业设计选题推荐:基于大数据的草鱼价格分析与可视化|毕业设计选题|计算机毕设|选题推荐|毕设指导|项目定制|源码|高质量项目
大数据·hadoop·python·数据挖掘·数据分析·课程设计·大数据毕设项目