进程:进程就是操作系统中执行的一个程序,操作系统以进程为单位分配存储空间,每个进程都有自己的地址空间、数据栈以及其他用于跟踪进程执行的辅助数据,操作系统管理所有进程的执行,为它们合理的分配资源。进程可以通过fork或spawn的方式来创建新的进程来执行其他的任务,不过新的进程也有自己独立的内存空间,因此必须通过进程间通信机制(IPC,Inter-Process Communication)来实现数据共享,具体的方式包括管道、信号、套接字、共享内存区等。
线程:一个进程还可以拥有多个并发的执行线索,简单的说就是拥有多个可以获得CPU调度的执行单元,这就是所谓的线程。由于线程在同一个进程下,它们可以共享相同的上下文,因此相对于进程而言,线程间的信息共享和通信更加容易。当然在单核CPU系统中,真正的并发是不可能的,因为在某个时刻能够获得CPU的只有唯一的一个线程,多个线程共享了CPU的执行时间。
Unix和Linux操作系统上提供了fork()
系统调用来创建进程,调用fork()
函数的是父进程,创建出的是子进程,子进程是父进程的一个拷贝,但是子进程拥有自己的PID。fork()
函数非常特殊它会返回两次,父进程中可以通过fork()
函数的返回值得到子进程的PID,而子进程中的返回值永远都是0。Python的os模块提供了fork()
函数。由于Windows系统没有fork()
调用,因此要实现跨平台的多进程编程,可以使用multiprocessing模块的Process
类来创建子进程,而且该模块还提供了更高级的封装,例如批量启动进程的进程池(Pool
)、用于进程间通信的队列(Queue
)和管道(Pipe
)等。
下面用一个下载文件的例子来说明使用多进程和不使用多进程到底有什么差别,先看看下面的代码。
# 普通单线程
from random import randint
from time import time, sleep
def download(filename):
print("开始下载%s: " % filename)
time_to_load = randint(3,6)
sleep(time_to_load)
print('%s下载完成! 耗费了%d秒' % (filename, time_to_load))
def main():
start = time()
download("Python从入门到吃土.avi")
download("Java从学习到入土.jpg")
end = time()
print("总共耗费%.2f秒." % (end-start))
if __name__ == '__main__':
main()
从上面的例子可以看出,如果程序中的代码只能按顺序一点点的往下执行,那么即使执行两个毫不相关的下载任务,也需要先等待一个文件下载完成后才能开始下一个下载任务,很显然这并不合理也没有效率。接下来我们使用多进程的方式将两个下载任务放到不同的进程中,代码如下所示。
from random import randint
from time impo