文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Python进程和线程的概念是什么

2023-06-02 02:07

关注

这篇文章主要介绍“Python进程和线程的概念是什么”,在日常操作中,相信很多人在Python进程和线程的概念是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python进程和线程的概念是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

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

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

进程的特点有:

一个进程还可以同时做多件事情,比如在 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 osfrom multiprocessing import Process, Poolfrom random import randintfrom 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.orgServer: 192.168.19.4Address: 192.168.19.4#53Non-authoritative answer:www.python.org canonical name = python.map.fastly.net.Name: python.map.fastly.netAddress: 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=mxpython.orgexit

运行结果:

$ nslookupServer: 192.168.19.4Address: 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.166mail.python.org has AAAA address 2001:888:2000:d::a6Exit code: 0

进程间通信

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

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

import osfrom multiprocessing import Process, Queueimport randomfrom 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: 24992Put A to queue...Process to read: 22836Get A from queue.Put B to queue...Get B from queue.Put C to queue...Get C from queue.

到此,关于“Python进程和线程的概念是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯