首页 > 系统相关 >python多进程踩过的坑

python多进程踩过的坑

时间:2023-05-21 21:24:37浏览次数:37  
标签:__ python self print 线程 进程 def

转载:python多进程踩过的坑 - 简书 (jianshu.com)

背景

算法离线测试的上线后,随着业务的增长,算法的构建越来越频繁,数量也越来越多,最近一个任务中就包含28个算法。
随着压力的增大,算法离线测试需要算法并行测试来解决效率问题。

成果

# 28个算法单进程执行时间
Task 1111417 runs 58800.14 seconds.
# 多进程执行时间
Task 1111573 runs 2187.91 seconds.
# 时间由16小时缩短到36分钟!!

多进程demo

一般的多进程代码

from multiprocessing import Pool
import os, time, random
def long_time_task(name):
    print('Run task %s (%s)...' % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('Task %s runs %0.2f seconds.' % (name, (end - start)))
    return 0, name

if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Pool()
    for i in range(4):
        p.apply_async(long_time_task, args=(i,))
    print('Waiting for all subprocesses done...')
    p.close()
    p.join()
    print('All subprocesses done.')

遇到的问题

  1. 类中不能直接使用 pool
class Foo():
    def work(self, i):
        print("this is work~~")
    def run(self):
        p = Pool()
        for i in range(4):
            p.apply_async(self.work, args=(i,))
        p.close()
        p.join()

if __name__ == '__main__':
    foo = Foo()
    foo.run()

直接使用会报错 cPickle.PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup builtin.instancemethod failed
查了下官方文档发现 python 默认只能 pickle 以下的类型:

  • None, True, and False
  • integers, floating point numbers, complex numbers
  • strings, bytes, bytearrays
  • tuples, lists, sets, and dictionaries containing only picklable objects
  • functions defined at the top level of a module (using def, not lambda)
  • built-in functions defined at the top level of a module
  • classes that are defined at the top level of a module
  • instances of such classes whose dict or the result of calling getstate() is picklable (see section -
  • Pickling Class Instances for details).

函数只能pickle在顶层定义的函数,很明显的class内的函数无法被pickle因此会报错。
这是 python2 才会遇到的问题,据说 python3 已经解决

解决方法
有很多种解决方法比如:

  • 调用pathos包下的multiprocessing模块代替原生的multiprocessing。pathos中multiprocessing是用dill包改写过的,dill包可以将几乎所有python的类型都serialize,因此都可以被pickle。
  • 使用线程代替进程 from multiprocessing.pool import ThreadPool as Pool
  • 可以使用 copy_reg 来规避异常
  • 把调用的函数写在顶层规避
  • 重写类的内部函数规避
## 调用函数写在顶层
def func(x):
    return x*x
class someClass(object):
    def __init__(self,func):
        self.f = func
    def go(self):
        p= Pool(processes=4)
        for i in range(4)
            p.apply_async(func, args=(10, ))
        p.close()
        p.join()
a=someClass(func)
a.go()
## 重写类的内部函数
class testClass():
    def upMethod(self, a):
        print 'I am UP:%s' %a
        time.sleep(1)
    def downMethod(self, b):
        print 'I am DOWN:%s' %b
        time.sleep(1)
    def multiProcess(self):
        p = Pool(2)
        aObj=p.apply_async(self, args=('up', 't1',))
        aObj=p.apply_async(self, args=('down', 't2',))
        p.close()
        p.join()
    def __call__(self,sign, *args, **kwds):
        if sign=='up':
                return self.upMethod(*args, **kwds)
        elif sign=='down':
                return self.downMethod(*args, **kwds)
if __name__=='__main__':
    testObj=testClass()
    testObj.multiProcess()
  1. apply_async 函数子进程不执行情况
  • 参数需要以元组的形式传递,并在最后一个参数后面加上 ,号,如果没有加,子进程不会执行
# 解决方法
p.apply_async(func, args=(url,)) #需要在参数后面添加逗号
  • 代码中有队列相关的操作时,也会引起子进程不执行的问题
    不要使用 multiProcess 中的 queue ,要用 manager 中的 queue 。
  • 子进程遇到错误挂掉,却不会抛出任何错误
# 一种
try:
    do some shit
except Exception e:
    print/log traceback.format_exc()
# 另一种
p = Pool(1)
result = p.apply_async(func, args=(arg,))
result = result.get(11)
p.terminate()
p.join()
print(result)
# multiprocessing.pool.AsyncResult.get(timeout)实现 function raise error 时自己也 raise error ,
# 然后通过 pool.terminate() 就能结束进程,还可以设置超时、返回 function return value
# 注意 这里是每个进程都等待 timeout 秒,多进程同时等待 timeout 应将 pool.apply_async() 改为使用 pool.map_async()
  1. 多进程间的数据共享
  • 多进程之间不能使用普通的Python数据类型,比如平常使用的list或者dict由父进程传递给子进程后,子进程只可读,写无效。
# 建议使用
from multiprocessing import Manager  
manager = Manager()
  • 数据多层嵌套无效
# 解决方法-重写嵌套层
mdict['aaa'] = {}
tmp = mdict['aaa'] 
tmp['bbb'] = 666
mdict['aaa'] = tmp
  • 进行 dump 等序列化操作报错
解决方法:1、初始化数据内容;2、强制转化 list() dict()

其他问题

为什么不用线程

  1. 效率不高
    实际操作对比发现,单线程10分钟任务,多线程花费8分钟,多进程花费5分钟。
  2. 原因:GIL锁:Global Interpreter Lock
    任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。
  3. 锁产生的原因
    多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
  4. 锁优缺点
    好处:确保了某段关键代码只能由一个线程从头到尾完整地执行
    坏处:首先是阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。
    其次,由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,导致多个线程全部挂起,既不能执行,也无法结束,只能靠操作系统强制终止。

 

    3人点赞   随笔    

作者:summertime_90d7
链接:https://www.jianshu.com/p/2e6d72ae1770
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

标签:__,python,self,print,线程,进程,def
From: https://www.cnblogs.com/zhiminyu/p/17419187.html

相关文章

  • Python跨进程共享数据/对象
    转载:(14条消息)Python跨进程共享数据/对象_python多进程共享对象_alpha.5的博客-CSDN博客1.跨进程共享方式在multiprocess库中,跨进程对象共享有三种方式:(1)第一种仅适用于原生机器类型,即python.ctypes当中的类型,这种在mp库的文档当中称为sharedmemory方式,即通过共享内存共享对......
  • Python字符串的encode与decode
    首先要搞清楚,字符串在Python内部的表示是unicode编码.因此,在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另一种编码。decode的作用是将其他编码的字符串转换成unicode编码,如str1.decode('gb2312'),表示将gb2312编......
  • python解析XML
    xml简介XML全称ExtensibleMarkupLanguage,中文译为可扩展标记语言。XML之前有两个先行者:SGML和HTML,率先登场的是SGML,尽管它功能强大,但文档结构复杂,既不容易学也不易于使用,因此几个主要的浏览器厂商均拒绝支持SGML,这些因素限制了SGML在网上的传播性;1989年HTML登场,它继......
  • python datetime时区转换
    比如把格林威治时间转换为上海时间:fromdatetimeimportdatetimeimportpytzprint('格林威治时间:',datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))print('上海时间:',datetime.now().astimezone(pytz.timezone("Asia/Shanghai")).strftime("......
  • Python 多进程之间共享变量
    转载:Python多进程之间共享变量-知乎(zhihu.com)Python多线程之间共享变量很简单,直接定义全局global变量即可。而多进程之间是相互独立的执行单元,这种方法就不可行了。不过Python标准库已经给我们提供了这样的能力,使用起来也很简单。但要分两种情况来看,一种是Process......
  • python类中调用类方法时,报错self参数未填
    转载:(14条消息)python类中调用类方法时,报错self参数未填。_追天一方的博客-CSDN博客又碰到了一个小错误比如一个类如下:classprint_number(object):def__init__(self,string="数字是"):self.string=stringdefprint_(self,ss=3):print("{}:{}".......
  • 网络编辑的使用和知识点,进程线程之间实现交互
    软件开放的框架c/s架构c就是Client客户端就是要去请求数据的s就是Server服务端就是给客服端根据客户的要求提供数据的服务端的必备条件时刻提供服务等待客服端的访问有一个固定的地址能够接受多个服务端的请求(高并发)B/s架构B就是Browser就是一个浏览器充当所有服务端......
  • 1.脚本高级命令,进程优先级命令,进程管理工具,任务相关命令
    一.总结脚本高级命令trap,install,mktemp,expect,进程优先级命令:nice,renice,进程管理工具:ps,pstree,prtstat,pgrep,pidof,uptime,mpstat,top,htop,free,pmap,vmstat,iostat,iotop,iftop,nload,nethogs,iptraf-ng,dstat,glances,cockpit,kill,job,任务......
  • 3.进程相关
    解析进程和线程的区别进程进程是一个具有独立功能的程序在一个数据集上的一次动态执行过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。进程是一种抽象的概念,从来没有统一的标准定义。进程一般由程序,数据集合和进程控制块三部分组成。进程具有的特性:动态性:......
  • java中使用jep调用python类
    经过调研,目前这应该只有一种调用方式了,那就是使用jep,后来亲测了以下确实是可行,我是使用jep调用了一个python文件中的类,并测试了类的一个方法,可以正常执行,但是具体速度会不会慢很多,我还没有测试。刚开始在调研的时候,说jython也可以调用,但是这个包只支持2.7python,毕竟现在很少有用2......