由于全局解释器锁的存在,python中的多线程使用有许多的局限性,因此部分场合使用多进程会优于多线程,比如在cpu计算密集型的环境下。
python中使用multiprocessing模块实现多进程。python多进程中引入了多线程中没有的Queue、Pool等模块,方便我们更好的使用多进程。
我们常说对于I/O密集型使用多线程,对于CPU密集型使用多进程,但其实I/O密集型的场景下也可以使用多进程,只不过多进程是十分耗费系统资源的,因此多线程是一个更优的选择。下面我们通过一段代码来感受一下多进程的优势

import threading
import multiprocessing
import time
# 向列表中添加1-100000000的数字,
def func(start,stop):
    lst = []
    for i in range(start,stop):
        lst.append(i)

if __name__ == "__main__":
	# 多线程添加
    thread_list = []
    start_time = time.time()
    t1 = threading.Thread(target=func,args=(1,50000000))
    t2 = threading.Thread(target=func,args=(50000000,100000001))
    thread_list.append(t1)
    thread_list.append(t2)
    t1.start()
    t2.start()
    for thread in thread_list:
        thread.join() # 阻塞主线程
    end_time = time.time()
    print("多线程用时:{}秒".format(end_time-start_time))

	# 单线程添加
    start_time = time.time()
    func(1,100000001)
    end_time = time.time()
    print("单线程用时:{}秒".format(end_time - start_time))
	
	# 多进程添加
    start_time = time.time()
    process_list = []
    m1 = multiprocessing.Process(target=func,args=(1,50000000))
    m2 = multiprocessing.Process(target=func,args=(50000000,100000001))
    process_list.append(m1)
    process_list.append(m2)
    m1.start()
    m2.start()
    for p in process_list:
        p.join()
    end_time = time.time()
    print("多进程用时:{}秒".format(end_time-start_time))

python多进程关闭 python多进程multiprocessing_python


想列表中添加1亿个数字,单线程用时18秒,多线程用时和单线程相比并没有什么优势的。多进程仅仅用时11秒,可以看出多进程在cpu密集型的情况下优势还是很明显的。

多进程的实现还可以通过继承的方式实现,

import multiprocessing
import time
class Myprocessing(multiprocessing.Process):
    def __init__(self,_start,stop): # 防止与内置方法重名,start加下划线或改名字
        super(Myprocessing, self).__init__()
        self._start = _start
        self.stop = stop
        
    # 复写父类的run方法
    def run(self):
        lst = []
        for i in range(self._start,self.stop):
            lst.append(i)

if __name__ == "__main__":
    start_time = time.time()
    process_list = []
    m1 = Myprocessing(1,50000000)
    m2 = Myprocessing(50000000,100000001)
    process_list.append(m1)
    process_list.append(m2)
    m1.start()
    m2.start()
    for p in process_list:
        p.join()
    end_time = time.time()
    print("多进程用时:{}秒".format(end_time - start_time))

python多进程关闭 python多进程multiprocessing_python_02


如果我们有100个任务需要处理,创建100个子进程是非常耗费资源的,我们可以指定10个子进程去处理这100个任务,python多进程中引入了进程池,创建进程池对象的时候可以指定一个最大进程数,当有新的请求提交到进程池中,如果池中的进程数还没有满,那么就会创建一个新的进程用来执行该请求, 但是如果池中的进程数满了,该请求就会等待,知道进程池中的进程有结束的了, 才会使用这个结束的进程来执行新的任务。

from multiprocessing import Pool # 导入进程池类
import time
def func(start,stop):
    lst = []
    for i in range(start,stop):
        lst.append(i)

if __name__ == "__main__":
    start_time = time.time()
    pool = Pool(2) # 创建进程池对象,最多创建两个子进程
    num_list = [(1,50000000),(50000000,100000001)]
    for num in num_list:
        pool.apply_async(func,num) # 添加子进程
    pool.close() # 等待所有子进程执行完毕关闭进程池
    pool.join() # 阻塞主进程,必须写在close()之后
    end_time = time.time()
    print("多进程用时:{}秒".format(end_time - start_time))

python多进程关闭 python多进程multiprocessing_python_03


进程可以理解为复制了一份程序加载到内存中,进程之间的执行是相互独立的,那么我们如何进行进程之间的通信呢?python多进程中有一个模块Queue队列就是来处理进程之间的通信的,队列是一种先进先出的数据结构。当两个进程进行通信时,只需要一个进程往queue队列里写内容,另一个进程从queue中取内容就可以了。

Queue有个参数maxsize,表示队列中允许的最大项数,默认为无限大。

Queue中有一些常用的参数:

put():向队列中存放数据,如果队列已满将阻塞,直到队列释放空间。

get():返回队列的一条数据,如果队列为空将阻塞,直到队列有数据可用为止。

qsize():获取队列中数据的数量。

empty():判断队列是否为空,返回True为空,返回False不为空。

full():判断队列是否已满,返回True已满,返回False未满。

get_nowait():取数据时不等待,如果队列为空直接抛出异常。

put_nowait():存数据时不等待,如果队列已满,直接抛出异常。

close():关闭队列。

import multiprocessing
import random
import time
def func1(queue):
    for i in range(1,6):
        sec = random.randrange(1,5)
        time.sleep(sec)
        queue.put(i) # 向队列中添加数据
        print("{} 第{}个数字加入队列".format(time.strftime("%H:%M:%S",time.localtime()),i))

def func2(queue):
    for i in range(1,6):
        queue.get() # 取出队列中的数据
        print("{} 取出队列中第{}个数字".format(time.strftime("%H:%M:%S"),i))

if __name__ == "__main__":
    queue = multiprocessing.Queue(3) # 队列中最多可添加三个数据
    m1 = multiprocessing.Process(target=func1,args=(queue,)) # 创建进程,并将queue作为参数传入
    m2 = multiprocessing.Process(target=func2,args=(queue,))
    m1.start() # 启动进程
    m2.start()