进程和线程(上)

点击上方“算法猿的成长”,选择“加为星标”

第一时间关注 AI 和 Python 知识

2019 年第 70 篇文章,总第 94 篇文章

本文大约 6000 字,阅读大约需要 15 分钟

最近会开始继续 Python 的进阶系列文章,这是该系列的第一篇文章,介绍进程和线程的知识,刚好上一篇文章就介绍了采用 concurrent.futures 模块实现多进程和多线程的操作,本文则介绍下进程和线程的概念,多进程和多线程各自的实现方法和优缺点,以及分别在哪些情况采用多进程,或者是多线程。

因为文章比较长,所以会分为两篇进行介绍

概念

并发编程就是实现让程序同时执行多个任务,而如何实现并发编程呢,这里就涉及到进程线程这两个概念。

对于操作系统来说,一个任务(或者程序)就是一个进程(Process),比如打开一个浏览器是开启一个浏览器进程,打开微信就启动了一个微信的进程,打开两个记事本,就启动两个记事本进程。

进程的特点有:

  • 操作系统以进程为单位分配存储空间, 每个进程有自己的地址空间、数据栈以及其他用于跟踪进程执行的辅助数据;

  • 进程可以通过 fork 或者 spawn 方式创建新的进程来执行其他任务

  • 进程都有自己独立的内存空间,所以进程需要通过进程间通信机制(IPC,Inter-Process Communication)来实现数据共享,具体的方式包括管道、信号、套接字、共享内存区

一个进程还可以同时做多件事情,比如在 Word 里面同时进行打字、拼音检查、打印等事情,也就是一个任务分为多个子任务同时进行,这些进程内的子任务被称为线程(Thread)

因为每个进程至少需要完成一件事情,也就是一个进程至少有一个线程。当要实现并发编程,也就是同时执行多任务时,有以下三种解决方案:

  • 多进程,每个进程只有一个线程,但多个进程一起执行多个任务;

  • 多线程,只启动一个进程,但一个进程内开启多个线程;

  • 多进程+多线程,即启动多个进程,每个进程又启动多个线程,但这种方法非常复杂,实际很少使用

注意:真正的并行执行多任务只有在多核 CPU 上才可以实现,单核 CPU 系统中,真正的并发是不可能的,因为在某个时刻能够获得CPU的只有唯一的一个线程,多个线程共享了CPU的执行时间

Python 是同时支持多进程和多线程的,下面就分别介绍多进程和多线程。

多进程

在 Unix/Linux 系统中,提供了一个 fork() 系统调用,它是一个特殊的函数,普通函数调用是调用一次,返回一次但 fork 函数调用一次,返回两次,因为调用该函数的是父进程,然后复制出一份子进程了,最后同时在父进程和子进程内返回,所以会返回两次。

子进程返回的永远是 0 ,而父进程会返回子进程的 ID,因为父进程可以复制多个子进程,所以需要记录每个子进程的 ID,而子进程可以通过调用 getpid() 获取父进程的 ID。

Python 中 os 模块封装了常见的系统调用,这就包括了 fork ,代码示例如下:

import osprint('Process (%s) start...' % os.getpid())
# Only works on Unix/Linux/Mac:
pid = os.fork()
if pid == 0:print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))
else:print('I (%s) just created a child process (%s).' % (os.getpid(), pid))

运行结果:

Process (876) start...
I (876) just created a child process (877).
I am child process (877) and my parent is 876.

由于 windows 系统中是不存在 fork ,所以上述函数无法调用,但 Python 是跨平台的,所以也还是有其他模块可以实现多进程的功能,比如 multiprocessing模块。

multiprocess

multiprocessing 模块中提供了 Process 类来代表一个进程对象,接下来用一个下载文件的例子来说明采用多进程和不用多进程的差别。

首先是不采用多进程的例子:

def download_task(filename):'''模拟下载文件'''print('开始下载%s...' % filename)time_to_download = randint(5, 10)sleep(time_to_download)print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))def download_without_multiprocess():'''不采用多进程'''start = time()download_task('Python.pdf')download_task('nazha.mkv')end = time()print('总共耗费了%.2f秒.' % (end - start))
if __name__ == '__main__':download_without_multiprocess()

运行结果如下,这里用 randint 函数来随机输出当前下载文件的耗时,从结果看,程序运行时间等于两个下载文件的任务时间总和。

开始下载Python.pdf...
Python.pdf下载完成! 耗费了9秒
开始下载nazha.mkv...
nazha.mkv下载完成! 耗费了9秒
总共耗费了18.00秒.

如果是采用多进程,例子如下所示:

def download_task(filename):'''模拟下载文件'''print('开始下载%s...' % filename)time_to_download = randint(5, 10)sleep(time_to_download)print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))def download_multiprocess():'''采用多进程'''start = time()p1 = Process(target=download_task, args=('Python.pdf',))p1.start()p2 = Process(target=download_task, args=('nazha.mkv',))p2.start()p1.join()p2.join()end = time()print('总共耗费了%.2f秒.' % (end - start))
if __name__ == '__main__':download_multiprocess()

这里多进程例子中,我们通过 Process 类创建了进程对象,通过 target 参数传入一个函数表示进程需要执行的任务,args 是一个元组,表示传递给函数的参数,然后采用 start 来启动进程,而 join 方法表示等待进程执行结束。

运行结果如下所示,耗时就不是两个任务执行时间总和,速度上也是大大的提升了。

开始下载Python.pdf...
开始下载nazha.mkv...
Python.pdf下载完成! 耗费了5秒
nazha.mkv下载完成! 耗费了9秒
总共耗费了9.36秒.
Pool

上述例子是开启了两个进程,但如果需要开启大量的子进程,上述代码的写法就不合适了,应该采用进程池的方式批量创建子进程,还是用下载文件的例子,但执行下部分的代码如下所示:

import os
from multiprocessing import Process, Pool
from random import randint
from time import time, sleepdef download_multiprocess_pool():'''采用多进程,并用 pool 管理进程池'''start = time()filenames = ['Python.pdf', 'nazha.mkv', 'something.mp4', 'lena.png', 'lol.avi']# 进程池p = Pool(5)for i in range(5):p.apply_async(download_task, args=(filenames[i], ))print('Waiting for all subprocesses done...')# 关闭进程池p.close()# 等待所有进程完成任务p.join()end = time()print('总共耗费了%.2f秒.' % (end - start))
if __name__ == '__main__':download_multiprocess_pool()

代码中 Pool 对象先创建了 5 个进程,然后 apply_async 方法就是并行启动进程执行任务了,调用 join() 方法之前必须先调用 close() ,close() 主要是关闭进程池,所以执行该方法后就不能再添加新的进程对象了。然后 join() 就是等待所有进程执行完任务。

运行结果如下所示:

Waiting for all subprocesses done...
开始下载Python.pdf...
开始下载nazha.mkv...
开始下载something.mp4...
开始下载lena.png...
开始下载lol.avi...
nazha.mkv下载完成! 耗费了5秒
lena.png下载完成! 耗费了6秒
something.mp4下载完成! 耗费了7秒
Python.pdf下载完成! 耗费了8秒
lol.avi下载完成! 耗费了9秒
总共耗费了9.80秒.
子进程

大多数情况,子进程是一个外部进程,而非自身。在创建子进程后,我们还需要控制子进程的输入和输出。

subprocess 模块可以让我们很好地开启子进程以及管理子进程的输入和输出。

下面是演示如何用 Python 演示命令 nslookup www.python.org,代码如下所示:

import subprocessprint('$ nslookup www.python.org')
r = subprocess.call(['nslookup', 'www.python.org'])
print('Exit code:', r)

运行结果:

$ nslookup www.python.org
Server:        192.168.19.4
Address:    192.168.19.4#53Non-authoritative answer:
www.python.org    canonical name = python.map.fastly.net.
Name:    python.map.fastly.net
Address: 199.27.79.223Exit code: 0

如果子进程需要输入,可以通过 communicate() 进行输入,代码如下所示:

import subprocessprint('$ nslookup')
p = subprocess.Popen(['nslookup'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate(b'set q=mx\npython.org\nexit\n')
print(output.decode('utf-8'))
print('Exit code:', p.returncode)

这段代码就是执行命令 nslookup 时,输入:

set q=mx
python.org
exit

运行结果:

$ nslookup
Server:        192.168.19.4
Address:    192.168.19.4#53Non-authoritative answer:
python.org    mail exchanger = 50 mail.python.org.Authoritative answers can be found from:
mail.python.org    internet address = 82.94.164.166
mail.python.org    has AAAA address 2001:888:2000:d::a6Exit code: 0
进程间通信

进程之间是需要通信的,multiprocess 模块中也提供了 QueuePipes 等多种方式来交换数据。

这里以 Queue 为例,在父进程创建两个子进程,一个往 Queue 写入数据,另一个从 Queue 读取数据。代码如下:

import os
from multiprocessing import Process, Queue
import random
from time import time, sleep# 写数据进程执行的代码:
def write(q):print('Process to write: %s' % os.getpid())for value in ['A', 'B', 'C']:print('Put %s to queue...' % value)q.put(value)sleep(random.random())# 读数据进程执行的代码:
def read(q):print('Process to read: %s' % os.getpid())while True:value = q.get(True)print('Get %s from queue.' % value)def ipc_queue():'''采用 Queue 实现进程间通信:return:'''# 父进程创建Queue,并传给各个子进程:q = Queue()pw = Process(target=write, args=(q,))pr = Process(target=read, args=(q,))# 启动子进程pw,写入:pw.start()# 启动子进程pr,读取:pr.start()# 等待pw结束:pw.join()# pr进程里是死循环,无法等待其结束,只能强行终止:pr.terminate()if __name__ == '__main__':ipc_queue()

运行结果如下所示:

Process to write: 24992
Put A to queue...
Process to read: 22836
Get A from queue.
Put B to queue...
Get B from queue.
Put C to queue...
Get C from queue.

参考

  • https://www.liaoxuefeng.com/wiki/1016959663602400/1017627212385376

  • https://github.com/jackfrued/Python-100-Days/blob/master/Day01-15/13.%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B.md

  • https://www.runoob.com/python3/python3-multithreading.html

本文主要是介绍进程和线程的概念,然后就是介绍多进程及其实现方式,在下一篇文章会介绍多线程的实现,以及两种方式应该如何选择。

代码已经上传到:

https://github.com/ccc013/Python_Notes/blob/master/Tutorials/Process_and_Threading/multi_process.py

欢迎关注我的微信公众号--算法猿的成长,或者扫描下方的二维码,大家一起交流,学习和进步!

640?wx_fmt=png

如果觉得不错,在看、转发就是对小编的一个支持!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/408557.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

你有哪些deep learning(rnn、cnn)调参的经验?

点击上方“算法猿的成长”,选择“加为星标”第一时间关注 AI 和 Python 知识来源:知乎问题深度学习中调参其实是一个比较重要的技巧,但很多时候都需要多尝试多积累经验,因此算法工程师也被调侃为调参工程师。这里分享来自知乎上的…

A+B for Matrices 及 C++ transform的用法

题目大意&#xff1a;给定两个矩阵&#xff0c;矩阵的最大大小是M*N&#xff08;小于等于10&#xff09;&#xff0c;矩阵元素的值的绝对值小于等于100&#xff0c;求矩阵相加后全0的行以及列数。 1 #include<iostream>2 using namespace std;3 #define N 104 5 int main…

进程和线程(下)

点击上方“算法猿的成长”&#xff0c;选择“加为星标”第一时间关注 AI 和 Python 知识2019 年第 71 篇文章&#xff0c;总第 95 篇文章本文大约 8000 字&#xff0c;建议收藏阅读上一篇文章介绍了进程和线程的基本概念&#xff0c;以及多进程如何实现&#xff0c;本文则介绍下…

8月总结抽奖

1首先是小小总结下 8 月发文的情况&#xff0c;总共推文时间是 21 天&#xff0c;原创文章有 9 篇&#xff0c;分别如下&#xff1a;Jupyter 进阶教程PyTorch 系列 | 数据加载和预处理教程PyTorch系列 | 如何加快你的模型训练速度呢&#xff1f;Leetcode 系列 | 反转链表PyTorc…

【文件系统】浅解释FAT32

了解完linux下的文件系统之后&#xff0c;顺便对FAT32也研究一下。 假如一个FAT32表如下所示。 文件的簇应该保留在目录中&#xff0c;根据此簇&#xff0c;应该能得到一个块。 要找到文件的下一块&#xff0c;就要根据簇在FAT中寻找&#xff0c;所以FAT中存储的不是本簇的簇号…

全网首发!2020年AI、CV、NLP顶会最全时间表!

点上方蓝字计算机视觉联盟获取更多干货在右上方 设为星标 ★&#xff0c;与你不见不散编辑&#xff1a;Sophia计算机视觉联盟 原创总结 | 公众号 CVLianMeng联盟花费一周对2020年顶会时间进行了总结&#xff01;2020 AI、CV、NLP顶会时间表&#xff0c;包含会议举办的时间、…

互联网大佬学历背景大揭秘,看看是你的老乡还是校友

作者&#xff1a;徐麟&#xff0c;某互联网公司数据分析狮&#xff0c;个人公众号数据森麟&#xff08;id&#xff1a;shujusenlin&#xff09;前言 互联网作为一个快速发展的新兴领域&#xff0c;聚集了大量的优秀人才&#xff0c;前沿技术的广泛应用也不断地为互联网注入着新…

Setup SQL Server 2008 Maintenance Plan Email Notifications

一条龙作完&#xff0c;如何设置EXCHANGE的操作员邮件通知。。 ~~~~ http://808techblog.com/2009/07/setup-sql-server-2008-maintena.html For most of the SQL installs that I maintain, nightly SQL dumps to disk and then copy to tape is my preferred backup method. …

PyTorch | 保存和加载模型教程

点击上方“算法猿的成长”&#xff0c;选择“加为星标”第一时间关注 AI 和 Python 知识图片来自 Unsplash&#xff0c;作者&#xff1a; Jenny Caywood 2019 年第 72 篇文章&#xff0c;总第 96 篇文章总共 7000 字&#xff0c;建议收藏阅读原题 | SAVING AND LOADING MODELS作…

都说dlib是人脸识别的神器,那到底能不能识破妖怪的伪装?

作者&#xff1a;盛光晓原文链接&#xff1a;https://blog.csdn.net/esa72ya/article/details/89189987众所周知&#xff0c;dlib是人脸识别的利器&#xff0c;被广泛应用于行为检测、安防工程、表情分析等&#xff0c;甚至还有学术界的前沿老师将这一技术用于上课点名&#xf…

国内有哪些不错的CV(计算机视觉)团队

点击上方“算法猿的成长”&#xff0c;选择“加为星标”第一时间关注 AI 和 Python 知识来源&#xff1a;知乎问题对于初入 CV 领域的同学&#xff0c;如果可以加入一个不错的团队&#xff0c;有好的导师带着&#xff0c;同时还有可以请教的师兄师姐&#xff0c;会加快入门 CV …

数据全裸时代,你的隐私有多容易获取?

大家好我是痴海&#xff0c;一位转型做增长的爬虫师&#xff0c;由于工作的缘故&#xff0c;对于身边许多信息都非常敏感。上个月朋友圈有很多人都在晒四六级成绩&#xff0c;有人欢喜有人忧愁&#xff0c;而我却感受到深深的恐惧。2018 年腾讯手机管家在一个报告中公布了一个数…