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