python 多进程和多线程的区别_单进程多线程

python 多进程和多线程的区别_单进程多线程在多核CPU的时代,使用多线程或多进程能够充分利用CPU多核性能来提高程序的执行效率,但Python的多线程为什么有时候耗时比单一线程更长,为什么多数情况下推荐使用Python多进程替代多线程,针对这些问题本文将重点介绍下Python的多进程和多线程区别和应用场景的选取。

基于Python的多进程和多线程对比和应用分析"

前言

在多核CPU的时代,使用多线程或多进程能够充分利用CPU多核性能来提高程序的执行效率,但Python的多线程为什么有时候耗时比单一线程更长,为什么多数情况下推荐使用Python多进程替代多线程,针对这些问题本文将重点介绍下Python的多进程和多线程区别和应用场景的选取。


进程和线程介绍

程序为存储在磁盘上的可执行文件,当把程序加载到内存中并被操作系统调用,则拥有了生命周期,进程即为运行中的程序。一个进程可以并行运行多个线程,每个线程执行不同的任务,也就是说线程是进程的组成部分。当一个进程启动时至少要执行一个任务,因此至少有一个主线程,由主线程再创建其他的子线程。多线程的执行方式和多进程相似,由操作系统在多个线程之间快速切换,让每个线程都短暂的交替运行,看起来像同时执行一样。当然,多核CPU可真正意义上实现多线程或多进程的同时执行。

python 多进程和多线程的区别_单进程多线程

进程和线程之间存在不同的特点。每个进程拥有自己的地址空间、内存和数据栈,由操作系统管理所有的进程,并为其合理分配执行时间。由于进程间资源相互独立,不同进程之间需要通过IPC(进程间通信)方式共享信息,但单个进程崩溃时不会导致系统崩溃。而多线程是在同一个进程下执行的,共享同一片数据空间,相比于进程而言,线程间的信息共享更加容易,但当一个线程崩溃时会导致整个进程崩溃。


Python GIL

Python代码的执行由Python解释器进行控制。目前Python的解释器有多种,如CPython、PyPy、Jython等,其中CPython为最广泛使用的Python解释器。理论上CPU是多核时支持多个线程同时执行,但在Python设计之初考虑到在Python解释器的主循环中执行Python代码,于是CPython中设计了全局解释器锁GIL(Global Interpreter Lock)机制()用于管理解释器的访问,Python线程的执行必须先竞争到GIL权限才能执行。因此无论是单核还是多核CPU,任意给定时刻只有一个线程会被Python解释器执行,这也是为什么在多核CPU上,Python的多线程有时效率并不高的根本原因。

注:关于Python解释器,简单的说,任何一种编程语言都需要用另一种语言来实现它,比如C语言是用机器语言来实现的。所以,Python作为一门编程语言,根据实现方式不同分为了CPyhton、Pypy、Jython等。


执行对比

Python多任务的解决方案主要由这么几种:

  • 启动多进程,每个进程只有一个线程,通过多进程执行多任务;
  • 启动单进程,在进程内启动多线程,通过多线程执行多任务;
  • 启动多进程,在每个进程内再启动多个线程,同时执行更多的任务;

由于第三种方法模型复杂,实际较少使用,本文针对前两种方案在双核CPU(Intel(R) Core(TM)2 Duo CPU E7500@ 2.93GHz)硬件平台上,Linux操作系统下使用Python2.7对计算密集型和I/O密集型任务进行执行效率测试。

(1)计算密集型测试。计算密集型任务的特点是需要进行大量的计算,在整个时间片内始终消耗CPU的资源。由于GIL机制的原因多线程中无法利用多核参与计算,但多线程之间切换的开销时间仍然存在,因此多线程比单一线程需要更多的执行时间。而多进程中有各自独立的GIL锁互不影响,可以充分利用多核参与计算,加快了执行速度。

测试代码如下:
#!/usr/bin/python
from threading import Thread
from multiprocessing import Process,Manager
from timeit import timeit
def count(n):
    while n > 0:
        n-=1
def test_normal():
    count(1000000)
    count(1000000)
def test_Thread():
    t1 = Thread(target=count,args=(1000000,))
    t2 = Thread(target=count,args=(1000000,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()            
def test_Thread():
    t1 = Thread(target=count,args=(1000000,))
    t2 = Thread(target=count,args=(1000000,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()        
def test_Process():
    t1 = Process(target=count,args=(1000000,))
    t2 = Process(target=count,args=(1000000,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()        
if __name__ == '__main__':
    print "test_normal",timeit('test_normal()','from __main__ import test_normal',number=10)
    print "test_Thread",timeit('test_Thread()','from __main__ import test_Thread',number=10)
    print "test_Process",timeit('test_Process()','from __main__ import test_Process',number=10)

执行结果如下:

test_normal 1.19412684441

test_Thread 1.88164782524

test_Process 0.687992095947

注:Join函数为逐个执行进程/线程,直到子进程/线程结束后主进程/线程才退出

(2)I/O密集型测试。I/O密集型任务的特点是CPU消耗很少,任务大部分时间都在等待I/O操作的完成(I/O速度远低于CPU和内存速度)。此处将count()函数内容替换为time.sleep(0.5),使用挂起方式模拟I/O阻塞。当挂起时I/O任务释放GIL,此时允许其他并发线程执行,提升了运行程序的效率。由于多进程创建和销毁的开销比多线程大,此处创建两个线程和进程时可发现线程执行效率更高,当线程和进程数量增加至100个时差距更加明显。

2个线程和进程测试代执行结果如下:

test_normal 10.0101339817

test_Thread 5.00919413567

test_Process 5.03114795685

增加至100个线程和进程测试代码:

def test_Thread():
    l = []
    for i in range(100):
        p = Thread(target=count,args=(1000000,))
        l.append(p)
        p.start()
    for j in l:
        j.join()
        
def test_Process():
    l = []
    for i in range(100):
        p = Process(target=count,args=(1000000,))
        l.append(p)
        p.start()
    for j in l:
        j.join()

执行结果如下:

test_Thread 5.15325403214

test_Process 5.84798789024


总结

由于Python的GIL限制,多线程更适合于I/O密集型应用(如典型的爬虫程序)。而对于计算密集型的应用,为了实现更好的并行性,可使用多进程以使CPU的其他内核加入执行。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13894.html

(0)

相关推荐

  • 数据采集介绍_数据收集的五种方法

    数据采集介绍_数据收集的五种方法1. 概述 现在学校越来越重视孩子课外知识的掌握,给孩子挑选课外书一般都是参考学校或者家长之间的推荐。 有时,也会想看看在儿童阶段,目前到底流行的是些什么样的书。 ​ 于是,就简单写了这个小爬虫,采集

    2023-04-30
    149
  • Zookeeper分布式协调即分布式锁机制[通俗易懂]

    Zookeeper分布式协调即分布式锁机制[通俗易懂]主要用到的Zookeeper机制: 临时+有序节点,节点watch机制 过程: 1. 每个客户端服务都到zookeeper的同一父节点下建立自己的临时+有序子节点,子节点名返回,各客户端保存在本地。

    2023-01-29
    152
  • 数据可视化之 Sankey 桑基图的实现[通俗易懂]

    数据可视化之 Sankey 桑基图的实现[通俗易懂]Google 搜索桑基图,可以搜到一大堆定义。简而言之,桑基图是一种数据流图,展示了数据是如何从左到右流向最后的节点,每条边代表一条数据流,宽度代表数据流的大小。桑基图常用于流量分析,可以很清楚的看出数据是如何渐渐分流的。本文着重讲解如何实现,理论方面的东西各位可以自行了解。 …

    2023-08-23
    138
  • 使用PyCharm编写Python代码

    使用PyCharm编写Python代码Python作为一种易于学习且功能强大的编程语言,已经被广泛应用于数据分析、科学计算和Web应用程序等领域。而在编写Python代码时,一个好的集成开发环境(IDE)是至关重要的。PyCharm是由JetBrains开发的一款强大的Python IDE,它拥有丰富的功能和智能的提示,可以提高编码效率,减少开发错误。

    2024-06-06
    62
  • mysql进阶知识_什么是MySQL

    mysql进阶知识_什么是MySQLMYSQL(进阶篇)——一篇文章带你深入掌握MYSQL 我们在上篇文章中已经学习了MYSQL的基本语法和概念 在这篇文章中我们将讲解底层结构和一些新的语法帮助你更好的运用MYSQL 温馨提醒:该文章大

    2023-05-30
    155
  • Python中的regexp_instr函数用法介绍

    Python中的regexp_instr函数用法介绍在编程领域中,处理或者查找字符串已经成为了日常工作的一部分,而正则表达式是处理字符串的最好工具之一。Python作为一个优秀的编程语言,自然也不例外。regexp_instr函数则是Python中正则表达式匹配工具之一。

    2024-08-12
    26
  • MySQL 事务管理「终于解决」

    MySQL 事务管理「终于解决」事务的4个特性(ACID) 原子性 Atomicity。每个事务中的操作,要么都成功,要么都失败 一致性 Consistency。事务执行前后,数据库中的数据应该保持一致 隔离性 Isolation。

    2023-02-04
    152
  • 用Python创建精美的画布

    用Python创建精美的画布画布是图形界面程序的核心,可以用于显示图形、图片、动画等,同时也可以用于数据可视化和图表绘制。下面我们将介绍如何用Python创建精美的画布,让你的程序更加生动有趣。主要包括以下几个方面:

    2024-02-14
    91

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注