python多进程使用的一些技巧和多线程、多进程的知识

文章目录

前言

使用多进程不一定非要用来并行加速,也可以方便地运行多个实验,这里对多进程、多线程的一些技巧和知识做一下记录。

正文

首先是一些知识的介绍。

apply是阻塞式的。首先主进程开始运行,碰到子进程,操作系统切换到子进程,等待子进程运行结束后,在切换到另外一个子进程,直到所有子进程运行完毕。然后在切换到主进程,运行剩余的部分。相当于在单进程中串行执行的

apply_async是异步非阻塞式的。他不会等待子进程执行完毕, 主进程会继续执行, 根据系统调度来进行进程切换,真正的并行

并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。

多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响;而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改。因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了,也就有了锁的诞生,确保原子性,即事务是一个不可再分割的工作单元,事务中的操作要么都发生,要么都不发生。多进程也可以设置一些共享的变量。

Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

GIL是Python解释器设计的历史遗留问题,通常我们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器。所以,在Python中,可以使用多线程,但不要指望能有效利用多核。如果一定要通过多线程利用多核,那只能通过C扩展来实现,不过这样就失去了Python简单易用的特点。不过,也不用过于担心,Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。

我们通过一个代码来介绍这些技巧,主要是两种方式,一种直接map传参,相当于对输入对象中的每个元素进行多进程运算;另一种是一个个进程异步地启动。

from multiprocessing import Pool

# 需要并行的函数
def f(x):
    return x*x

def cmp_string(s1):
    print(s1 is 'abc')

if __name__ == '__main__':
	'''
	设置进程池,它提供了一种快捷的方法,赋予函数并行化处理一系列输入值的能力,
	可以将输入数据分配给不同进程处理(数据并行)。
	不设置默认根据本地的cpu个数决定,processes小于等于本地的cpu个数;
	如果池中的进程数已经达到规定最大值,那么该请求就会等待,
	直到池中有进程结束,才会创建新的进程来执行它。
	'''
	with Pool(5) as p:
        print(p.map(f, [1, 2, 3])) # 传参
    '''
    输出为:[1, 4, 9]
    '''
    p = Pool(12) # 设置进程池,最多并行12个
    x = 'abc'
    for i in range(2):
    	# 异步地启动一个进程,参数为x。这里逗号不能省略,否则会解析为3个参数
        res = p.apply_async(cmp_string, args=(x,))
        # 获取多进程的错误信息,否则报错了也不会显示。可以试试把上面的逗号去掉,看错误信息
        res.get()
    print('子进程开始...')
    # 关闭进程池,不会有新的进程加入到pool
    p.close()
    # 主进程本身很短,join函数等待所有子进程结束之后主进程才继续运行
    p.join()
    print('所有子进程结束!')

这里可以看出cmp_string的结果是False,如果不清楚判断string的方式,可以看这篇文章

猜你喜欢

转载自blog.csdn.net/qq_38163755/article/details/115102394