资讯专栏INFORMATION COLUMN

python综合学习二之多进程

gityuan / 2039人阅读

摘要:本节讲学习的多进程。和之前的的不同点是丢向的函数有返回值,而的没有返回值。所以接下来让我们来看下这两个进程是否会出现冲突。

本节讲学习Python的多进程。
一、多进程和多线程比较

多进程 Multiprocessing 和多线程 threading 类似, 他们都是在 python 中用来并行运算的. 不过既然有了 threading, 为什么 Python 还要出一个 multiprocessing 呢? 原因很简单, 就是用来弥补 threading 的一些劣势, 比如在 threading 教程中提到的GIL.

使用 multiprocessing 也非常简单, 如果对 threading 有一定了解的朋友, 你们的享受时间就到了. 因为 python 把 multiprocessing 和 threading 的使用方法做的几乎差不多. 这样我们就更容易上手. 也更容易发挥你电脑多核系统的威力了!

二、添加进程Process
import multiprocessing as mp
import threading as td

def job(a,d):
    print("aaaaa")

t1 = td.Thread(target=job,args=(1,2))
p1 = mp.Process(target=job,args=(1,2))
t1.start()
p1.start()
t1.join()
p1.join()

从上面的使用对比代码可以看出,线程和进程的使用方法相似。

使用

在运用时需要添加上一个定义main函数的语句

if __name__=="__main__":

完整的应用代码:

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: process_test.py
@time: 18/8/26 01:12
"""

import multiprocessing as mp

def job(a, d):
    print a, d

if __name__ == "__main__":
    p1 = mp.Process(target=job, args=(1, 2))
    p1.start()
    p1.join()

运行环境要在terminal环境下,可能其他的编辑工具会出现运行结束后没有打印结果,在terminal中的运行后打印的结果为:

➜  baseLearn python ./process/process_test.py
1 2
➜  baseLearn
三、存储进程输出Queue

Queue的功能是将每个核或线程的运算结果放在队里中, 等到每个线程或核运行完毕后再从队列中取出结果, 继续加载运算。原因很简单, 多线程调用的函数不能有返回值, 所以使用Queue存储多个线程运算的结果

process_queue.py

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: process_queue.py
@time: 18/8/26 01:12
"""

import multiprocessing as mp

# 定义一个被多线程调用的函数,q 就像一个队列,用来保存每次函数运行的结果
def job(q):
    res = 0
    for i in range(1000):
        res += i + i**2 + i**3
    q.put(res)   #queue

if __name__ == "__main__":
    q = mp.Queue()
    p1 = mp.Process(target=job, args=(q,))
    p2 = mp.Process(target=job, args=(q,))

    # 分别启动、连接两个线程
    p1.start()
    p2.start()
    p1.join()
    p2.join()

    # 上面是分两批处理的,所以这里分两批输出,将结果分别保存
    res1 = q.get()
    res2 = q.get()

    print res1,res2

打印输出结果:

➜ python ./process/process_queue.py
249833583000 249833583000
四、进程池

进程池就是我们将所要运行的东西,放到池子里,Python会自行解决多进程的问题

1、导入多进程模块

首先import multiprocessing 和定义job()

import multiprocessing as mp

def job(x):
    return x*x
2、进程池Pool()和map()

然后我们定义一个Pool

pool = mp.Pool()

有了池子之后,就可以让池子对应某一个函数,我们向池子里丢数据,池子就会返回函数返回的值。 Pool和之前的Process的不同点是丢向Pool的函数有返回值,而Process没有返回值

接下来用map()获取结果,在map()中需要放入函数和需要迭代运算的值,然后它会自动分配给CPU核,返回结果

res = pool.map(job, range(10))

让我们来运行一下

def multicore():
    pool = mp.Pool()
    res = pool.map(job, range(10))
    print(res)
    
if __name__ == "__main__":
    multicore()

完成代码:

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: process_queue.py
@time: 18/8/26 01:12
"""

import multiprocessing as mp

def job(x):
    return x*x  # 注意这里的函数有return返回值

def multicore():
    pool = mp.Pool()
    res = pool.map(job, range(10))
    print(res)
    
if __name__ == "__main__":
    multicore()

执行结果:

➜  baseLearn python ./process/process_pool.py
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
3、自定义核数量

我们怎么知道Pool是否真的调用了多个核呢?我们可以把迭代次数增大些,然后打开CPU负载看下CPU运行情况

打开CPU负载(Mac):活动监视器 > CPU > CPU负载(单击一下即可)

Pool默认大小是CPU的核数,我们也可以通过在Pool中传入processes参数即可自定义需要的核数量

def multicore():
    pool = mp.Pool(processes=3) # 定义CPU核数量为3
    res = pool.map(job, range(10))
    print(res)
4、apply_async()

Pool除了map()外,还有可以返回结果的方式,那就是apply_async().

apply_async()只能传递一个值,它只会放入一个核进行运算,但是传入值时要注意是可迭代的,所以在传入值后需要加逗号, 同时需要用get()方法获取返回值

def multicore():
    pool = mp.Pool() 
    res = pool.map(job, range(10))
    print(res)
    res = pool.apply_async(job, (2,))
    # 用get获得结果
    print(res.get())

运行结果;

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]  # map()
4 # apply_async()
总结

Pool默认调用是CPU的核数,传入processes参数可自定义CPU核数

map() 放入迭代参数,返回多个结果

apply_async()只能放入一组参数,并返回一个结果,如果想得到map()的效果需要通过迭代

五、共享内存shared memory

这节我们学习如何定义共享内存。只有用共享内存才能让CPU之间有交流

Shared Value

我们可以通过使用Value数据存储在一个共享的内存表中。

import multiprocessing as mp

value1 = mp.Value("i", 0) 
value2 = mp.Value("d", 3.14)

其中di参数用来设置数据类型的,d表示一个双精浮点类型 double,i表示一个带符号的整型

Type code C Type Python Type Minimum size in bytes
"b" signed char int 1
"B" unsigned char int 1
"u" Py_UNICODE Unicode character 2
"h" signed short int 2
"H" unsigned short int 2
"i" signed int int 2
"I" unsigned int int 2
"l" signed long int 4
"L" unsigned long int 4
"q" signed long long int 8
"Q" unsigned long long int 8
"f" float float 4
"d" double float 8
Shared Array

在Python的 mutiprocessing 中,有还有一个Array类,可以和共享内存交互,来实现在进程之间共享数据

array = mp.Array("i", [1, 2, 3, 4])

这里的Array和numpy中的不同,它只能是一维的,不能是多维的。同样和Value 一样,需要定义数据形式,否则会报错。 我们会在后一节举例说明这两种的使用方法.

错误形式

array = mp.Array("i", [[1, 2], [3, 4]]) # 2维list

"""
TypeError: an integer is required
"""
六、进程锁Lock 不加进程锁

让我们看看没有加进程锁时会产生什么样的结果。

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: process_no_lock.py
@time: 18/8/26 09:22
"""

import multiprocessing as mp
import time

def job(v, num):
    for _ in range(5):
        time.sleep(0.5) # 暂停0.5秒,让输出效果更明显
        v.value += num  # v.value获取共享变量值
        print(v.value)

def multicore():
    v = mp.Value("i", 0)  # 定义共享变量
    p1 = mp.Process(target=job, args=(v, 1))
    p2 = mp.Process(target=job, args=(v, 4)) # 设定不同的number看如何抢夺内存
    p1.start()
    p2.start()
    p1.join()
    p2.join()

if __name__ == "__main__":
    multicore()

在上面的代码中,我们定义了一个共享变量v,两个进程都可以对它进行操作。 在job()中我们想让v每隔0.1秒输出一次累加num的结果,但是在两个进程p1p2 中设定了不同的累加值。所以接下来让我们来看下这两个进程是否会出现冲突。

结果打印:

➜  baseLearn python ./process/process_no_lock.py
1
5
9
9
13
13
17
17
18
18
➜  baseLearn

我们可以看到,进程1和进程2在相互着使用共享内存v

加进程锁

为了解决上述不同进程抢共享资源的问题,我们可以用加进程锁来解决。

首先需要定义一个进程锁

 l = mp.Lock() # 定义一个进程锁

然后将进程锁的信息传入各个进程中

p1 = mp.Process(target=job, args=(v,1,l)) # 需要将Lock传入
p2 = mp.Process(target=job, args=(v,3,l)) 

job()中设置进程锁的使用,保证运行时一个进程的对锁内内容的独占

def job(v, num, l):
    l.acquire() # 锁住
    for _ in range(5):
        time.sleep(0.1) 
        v.value += num # v.value获取共享内存
        print(v.value)
    l.release() # 释放

全部代码:

# -*- coding:utf-8 -*-

"""
@author: Corwien
@file: process_lock.py
@time: 18/8/26 09:22
"""

import multiprocessing as mp
import time

def job(v, num, l):
    l.acquire() # 锁住
    for _ in range(5):
        time.sleep(0.5) # 暂停0.5秒,让输出效果更明显
        v.value += num  # v.value获取共享变量值
        print(v.value)
    l.release() # 释放

def multicore():
    l = mp.Lock() # 定义一个进程锁
    v = mp.Value("i", 0)  # 定义共享变量
    p1 = mp.Process(target=job, args=(v, 1, l)) # 需要将lock传入
    p2 = mp.Process(target=job, args=(v, 4, l)) # 设定不同的number看如何抢夺内存
    p1.start()
    p2.start()
    p1.join()
    p2.join()

if __name__ == "__main__":
    multicore()

运行一下,让我们看看是否还会出现抢占资源的情况:

结果打印:

➜  baseLearn python ./process/process_lock.py
1
2
3
4
5
9
13
17
21
25

显然,进程锁保证了进程p1的完整运行,然后才进行了进程p2的运行

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/42314.html

相关文章

  • python综合学习之多线程

    摘要:如下面的例子,在学习线程时,将文件名命名为脚本完全正常没问题,结果报下面的错误。最大的问题就是的多线程程序并不能利用多核的优势比如一个使用了多个线程的计算密集型程序只会在一个单上面运行。 本文记录学习Python遇到的问题和一些常用用法,注本开发环境的Python版本为2.7。 一、python文件命名 在python文件命名时,一定要注意不能和系统默认的模块名冲突,否则会报错。如下面...

    cjie 评论0 收藏0
  • 零基础如何学爬虫技术

    摘要:楚江数据是专业的互联网数据技术服务,现整理出零基础如何学爬虫技术以供学习,。本文来源知乎作者路人甲链接楚江数据提供网站数据采集和爬虫软件定制开发服务,服务范围涵盖社交网络电子商务分类信息学术研究等。 楚江数据是专业的互联网数据技术服务,现整理出零基础如何学爬虫技术以供学习,http://www.chujiangdata.com。 第一:Python爬虫学习系列教程(来源于某博主:htt...

    KunMinX 评论0 收藏0
  • 云数据的三大安全威胁

    摘要:对云数据安全来说,也是一样的。行业在保护企业内部数据安全方面,已经有几十年的经验了。行业对云数据安全的经验还太少。据统计,类似这样的用户误操作占数据损失的三分之一到三分之二之多。由于云应用的日益复杂,以及行业还缺乏关于云安全的统一的标准。 任何一个惊悚电影的爱好者都会告诉你, 真正的恐怖在于你无法预计什么时候,怎么样的灾难会发生。 对云数据安全来说,也是一样的。 IT行业在保护企业内部数据安...

    sydMobile 评论0 收藏0
  • Python爬虫学习路线

    摘要:以下这些项目,你拿来学习学习练练手。当你每个步骤都能做到很优秀的时候,你应该考虑如何组合这四个步骤,使你的爬虫达到效率最高,也就是所谓的爬虫策略问题,爬虫策略学习不是一朝一夕的事情,建议多看看一些比较优秀的爬虫的设计方案,比如说。 (一)如何学习Python 学习Python大致可以分为以下几个阶段: 1.刚上手的时候肯定是先过一遍Python最基本的知识,比如说:变量、数据结构、语法...

    liaoyg8023 评论0 收藏0
  • Python爬虫利器二之Beautiful Soup的用法

    摘要:官方解释如下提供一些简单的式的函数用来处理导航搜索修改分析树等功能。废话不多说,我们来试一下吧安装目前已经停止开发,推荐在现在的项目中使用,不过它已经被移植到了,也就是说导入时我们需要。 上一节我们介绍了正则表达式,它的内容其实还是蛮多的,如果一个正则匹配稍有差池,那可能程序就处在永久的循环之中,而且有的小伙伴们也对写正则表达式的写法用得不熟练,没关系,我们还有一个更强大的工具,叫Be...

    cjie 评论0 收藏0

发表评论

0条评论

gityuan

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<