Python多进程和多线程总结

一、什么是进程(process)和线程(thread)

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程不能独立存在,必须依赖进程存在,一个进程至少有一个线程。线程是CPU调度和分配的最小单位。一个线程就是一堆指令集合。

看过一篇对进程、线程比较形象的解释的文章,链接如下:
http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

二、Python多进程

python提供multiprocessing用于创建多进程。

创建进程(两种方式):
  • 实例化一个multiprocessing.Process的对象,并传入一个初始化函数对象作为新建进程执行入口:
import time
from multiprocessing import Process

def f(name):
    time.sleep(1)
    print('hello', name)

if __name__ == '__main__':
    p_list = []
    for i in range(3):
        p = Process(target=f, args=('letme',))
        p_list.append(p)
        p.start()
    for p in p_list:
        p.join()

    print('end')
  • 自定义MyProcess类,继承multiprocessing.Process并重写run方法:
import datetime
from multiprocessing import Process

class MyProcess(Process):
    def __init__(self, name):
        super(MyProcess, self).__init__()
        self.name = name

    def run(self):
        time.sleep(1)
        print('hello', self.name, datetime.datetime.now())

if __name__ == '__main__':
    p_list = []
    for i in range(3):
        p = MyProcess('letme')
        p_list.append(p)
        p.start()

    for p in p_list:
        p.join()

    print('end')
GIL(Global Interpreter Lock)

在非python环境中,单核情况下,同时只能有一个任务执行。多核时可以支持多个线程同时执行。但是在python中,无论有多少核,同时只能执行一个线程。究其原因,这就是由于GIL的存在导致的。
GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。GIL只在cpython中才有,因为cpython调用的是c语言的原生线程,所以他不能直接操作cpu,只能利用GIL保证同一时间只能有一个线程拿到数据。而在pypy和jpython中是没有GIL的。

三、多线程

Python的标准库提供了两个模块:_threadthreading_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。

创建线程(两种方式):
  • 实例化一个threading.Thread的对象,并传入一个初始化函数对象作为线程执行的入口:
import threading
import time

begin = time.time()

def foo(n):
    print('foo')
    time.sleep(1)
def bar(n):
    print('bar')
    time.sleep(2)

t1 = threading.Thread(target=foo, args=(1,))
t2 = threading.Thread(target=bar, args=(2,))

t1.start()
t2.start()

t1.join()
t2.join()

end = time.time()
print(end-begin)
  • 自定义MyThread类,继承threading.Thread,并重写run函数:
import threading
import time

class MyThread(threading.Thread):
    def __init__(self, num):
        super(MyThread, self).__init__()
        self.num = num

    def run(self):  # 定义每个线程要运行的函数
        print("running on number:%s" % self.num)
        time.sleep(3)

if __name__ == '__main__':

    t1 = MyThread(1)
    t2 = MyThread(2)

    t1.start()
    t2.start()

三、Python多进程和多线程比较

线程与进程区别

  • 进程是资源分配的基本单位,线程是CPU执行和调度的基本单位;

  • 通信/同步方式:
    进程:

    • 通信方式:管道,FIFO,消息队列,信号,共享内存,socket,stream流;
    • 同步方式:PV信号量,管程

    线程:

    • 同步方式:互斥锁,递归锁,条件变量,信号量
    • 通信方式:位于同一进程的线程共享进程资源,因此线程间没有类似于进程间用于数据传递的通信方式,线程间的通信主要是用于线程同步。

由于GIL的存在,很多人认为Python多进程编程更快,针对多核CPU,理论上来说也是采用多进程更能有效利用资源。

  • 对CPU密集型代码(比如循环计算) - 多进程效率更高
    缺陷:多个进程之间通信成本高,切换开销大。
  • 对IO密集型代码(比如文件操作,网络爬虫) - 多线程效率更高。
    缺陷:同一个时间切片只能运行一个线程,不能做到高并行,但是可以做到 高并发。

为什么是这样呢?其实也不难理解。对于IO密集型操作,大部分消耗时间其实是等待时间,在等待时间中CPU是不需要工作的,那你在此期间提供双CPU资源也是利用不上的,相反对于CPU密集型代码,2个CPU干活肯定比一个CPU快很多。那么为什么多线程会对IO密集型代码有用呢?这时因为python碰到等待会释放GIL供新的线程使用,实现了线程间的切换。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值