Python多进程

Posted magicdata

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python多进程相关的知识,希望对你有一定的参考价值。

多线程的使用

关于线程、进程以及协程我相信在所有的语言中都会涉及到,它们的功能非常强大,我对于这三种的学习也不够深,在今后的生活中会一直学习下去,今天我就先把我学到的记录一下,时刻勉励自己学习。

线程

多线程类似同时执行多个不同的程序,每个独立的线程都有一个程序的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。线程的概念理解起来可能比较抽象,它是操作系统能够进行运算调度的最小单位。本质上是一串指令的集合。

在python3中,我们使用threading模块来支持多线程,那么我们先来写一个最简单的多线程:

import threading
import time
def run1():
    print('我是第一个线程')
    time.sleep(2)
def run2():
    print('我是第二个线程')
    time.sleep(2)
start_time=time.time()
t1=threading.Thread(target=run1)
t2=threading.Thread(target=run2)
t1.start()
t2.start()
t1.join()
t2.join()
end_time=time.time()
print(end_time-start_time)
output:
我是第一个线程
我是第二个线程
2.0031144

在这个实例中,我们一步步来解析threading模块的作用:

  1. threading.Thread(target=目标函数,*args, **kwargs)这个方法会创建一个线程对象,target为该线程要执行的目标函数,*args为参数,它必须为一个元祖,**kwargs为参数,它是一个字典类型的。这个方法会返回一个线程对象。
  2. t1.start() t2.start()这个方法为启动该线程。
  3. t1.join() t2.join() 这个方法的意义在于主线程必须要等到该线程执行完毕后才能结束,即子线程没有结束则该程序永远不能结束。

当然有人会好奇,这样比不用多线程真的快吗?我们来试试利用单线程完成这个程序:

import time
def run1():
    print('我是第一个线程')
    time.sleep(2)
def run2():
    print('我是第二个线程')
    time.sleep(2)
start_time=time.time()
run1()
run2()
end_time=time.time()
print(end_time-start_time)
output:
我是第一个线程
我是第二个线程
4.00122

很明显可以看到利用多线程比单线程确实有很大优势。

另外,多线程中对于子线程的处理还有一种方法叫做守护线程,所谓守护线程,就是当该程序的主线程结束时,子线程也全部强制结束。我们再写个简单的例子:

import threading,time
def run1():
    print('第一个功能')
    time.sleep(1)
    print('第二个功能')
t1=threading.Thread(target=run1)
t1.setDaemon(True)
t1.start()
print('主线程结束了')
output:
第一个功能
主线程结束了

设置子线程守护的方法为setDaemon方法,线程调用该方法并传入参数True,即可设置该线程为守护线程。

在多线程任务中,还存在一些问题。多线程在程序中是共享变量资源的,在不同线程同时对同一个变量进行修改的时候,可能就会遇到一些未知的错误。此时,就需要引入另一个功能模块,叫做互斥锁。互斥锁的原理是将最核心的变量资源加锁,在一个线程对该资源进行操作时,其余线程不得访问该资源。 在threading模块中定义了Lock类

import threading
lock=threading.lock()

那么我们该如何利用这个锁尼?

import threading
lock=threading.lock()   #创建锁
lock.acquire()   #上锁
#对变量(资源)进行访问、修改
lock.release() # 解锁  修改完成后一定要解锁 不让会出现死锁现象

这里要注意的是在使用完毕后要及时释放资源,否则会导致死锁现象。

GIL锁

其实,通过这个例子可以看出来,一旦引入了互斥锁,会发现这个多线程真正意义上来说是一种伪多线程,它不能做到真正的多个线程同时进行,这和python的底层解释器GIL有关。

这里引入的GIL,称为全局解释器锁。Python代码的执行由Python虚拟机(解释器)来控制。Python在设计之初就考虑要在主循环中,同时只有一个线程在执行,就像单CPU的系统中运行多个进程那样,内存中可以存放多个程序,但任意时刻,只有一个程序在CPU中运行。同样地,虽然Python解释器可以运行多个线程,只有一个线程在解释器中运行。

对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同时只有一个线程在运行。在多线程环境中,Python虚拟机按照以下方式执行。

  1. 设置GIL。
  2. 切换到一个线程去执行。
  3. 运行。
  4. 把线程设置为睡眠状态。
  5. 解锁GIL。
  6. 再次重复以上步骤。

对所有面向I/O的(会调用内建的操作系统C代码的)程序来说,GIL会在这个I/O调用之前被释放,以允许其他线程在这个线程等待I/O的时候运行。如果某线程并未使用很多I/O操作,它会在自己的时间片内一直占用处理器和GIL。也就是说,I/O密集型的Python程序比计算密集型的Python程序更能充分利用多线程的好处。

那么我们要如何改善python的多线程,真正实现多核的利用尼?

第一个:更换底层GIL解释器。这个对于初学者来说了解得比较少,不建议这么做。

第二个:利用python的多进程来替换多线程,不同python进程中有各自独立的GIL锁,互不影响。

第三个:利用C语言来写计算密集型任务,然后把.so链接库内容加载到python中,因为执行C代码,GIL锁会释放,这样可以做到每个核跑一个线程。

所以,建议各位小伙伴们在掌握python后,再学一下C语言。

以上是关于Python多进程的主要内容,如果未能解决你的问题,请参考以下文章

[Python3] 043 多线程 简介

python中的多线程和多进程编程

代码片段:Shell脚本实现重复执行和多进程

线程学习知识点总结

什么是多线程,多进程?

多线程编程