一、什么是进程(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的标准库提供了两个模块:_thread
和threading
,_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供新的线程使用,实现了线程间的切换。