python3 多进程求助 OSError: [Errno 24] Too many open files - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
css3
V2EX    程序员

python3 多进程求助 OSError: [Errno 24] Too many open files

  •  
  •   css3 2021-03-30 10:47:24 +08:00 4424 次点击
    这是一个创建于 1726 天前的主题,其中的信息可能已经有所发展或是发生改变。

    业务:有大量(本次测试时 1 万多张)图片需要转成 base64 编码后,送入 http 接口请求处理,我采用以下代码: base64 用生成器处理, request 用多进程。 但下面代码跑到一半的时候,直接抛了 OSError: [Errno 24] Too many open files, 百度了一下,看上去是进程超过所能开启的最大文件数了, ulimit -n # mac 8192 请教下各位,我怎么应该 fix 这个问题,最终需求就是想快速高效的完成这个操作,可能我写的代码一开始就有问题,还希望大佬们指点一下。

    import json import time import requests import base64 import os from multiprocessing import Process def img_to_base64(img_path): r = {} for root, dirs, files in os.walk(img_path): for pic in files: if pic.endswith(('.png', '.jpg', '.jpeg', '.tiff', '.bmp', '.gif')): img = os.path.join(root, pic) with open(img, 'rb') as f: bs64 = base64.b64encode(f.read()).decode('utf-8') r[img] = bs64 yield r def req(host, img_path): bs64_generator = img_to_base64(img_path) procs = [] for items in bs64_generator: body, pic = None, None for pic, base64 in items.items(): body = { "requests": [ { "resource": { "base64": base64 } } ] } p = Process(target=r, args=(host, body, pic)) procs.append(p) p.start() for proc in procs: proc.join() def r(host, body, img): url = f'http://{host}/demo/' r = requests.post(url, data=json.dumps(body)) print(img, r.json().get('results')) ret = r.json().get('results')[0]['status'] if ret != 'OK': print(img, ret) req('10.10.23.17:3345', './mypic/') 
    32 条回复    2021-03-31 18:43:37 +08:00
    BrettD
        1
    BrettD  
       2021-03-30 10:50:11 +08:00 via iPhone
    ulimit 不行吗
    F281M6Dh8DXpD1g2
        2
    F281M6Dh8DXpD1g2  
       2021-03-30 10:53:46 +08:00 via iPhone   2
    yield 那里缩进不对
    CRVV
        3
    CRVV  
       2021-03-30 10:55:17 +08:00
    1. 发 HTTP 请求不需要用多进程
    2. 如果在乎性能,请用 requests.session
    3. 如果单线程顺序发请求不够快,可以用 ThreadPoolExecutor 或者 aiohttp
    cocowind
        4
    cocowind  
       2021-03-30 10:56:12 +08:00
    做个缓冲区....要限制一下并行的进程,句柄数...你这能跑到一半儿,那电脑也是非常不戳
    UN2758
        5
    UN2758  
       2021-03-30 11:00:27 +08:00
    你定义的 img_to_base64 是单纯的生成器,想要协程支持的生成器,得给它加上 @asyncio.coroutine
    UN2758
        6
    UN2758  
       2021-03-30 11:02:48 +08:00
    @UN2758 #5 看错
    xiaolinjia
        7
    xiaolinjia  
       2021-03-30 11:07:01 +08:00
    开了 1w 多进程牛的,整个 multiprocessing.Pool 进程池限制下并行数吧。
    css3
        8
    css3  
    OP
       2021-03-30 11:24:33 +08:00
    @BrettD mac 8192 跑满了

    @liprais 这个缩进应该没问题吧,每处理 1 张,yield 出来

    @CRVV 为啥不需要用多进程呢

    @sss495088732 不知道咋搞缓冲区


    @UN2758 老哥想表达啥,我没有看太懂啊

    @xiaolinjia 好的,多谢,我找下文档
    ch2
        9
    ch2  
       2021-03-30 12:14:41 +08:00
    1 万个任务正常做法是用进程池开 n 个进程(n<=你的 cpu 核心数*2)
    分批陆续完成,而不是 1 万个进程一拥而上
    而且你的这个代码瓶颈在网速上,根本不需要多进程,用多线程就能处理
    但是最优的做法是用 grequests 一次批量发 1000 个请求,配合上错误重试,分 10-20 次就搞定了
    ch2
        10
    ch2  
       2021-03-30 12:18:40 +08:00
    随便找到网图,grequests 是编程最简单的大批量发 http 请求的方法,几乎没有唯二的其他选择,你会用 requests 就会用 grequests
    hsfzxjy
        11
    hsfzxjy  
       2021-03-30 12:19:05 +08:00 via Android
    @css3 你要等文件关了再 yield
    momo1999
        12
    momo1999  
       2021-03-30 12:48:53 +08:00
    开 100 个进程差不多了,再多也不快。
    zonyitoo
        13
    zonyitoo  
       2021-03-30 13:24:32 +08:00
    每个图标单独开一个进程去跑,这居然能跑得动电脑的配置也着实厉害了
    renmu123
        14
    renmu123  
       2021-03-30 13:26:30 +08:00 via Android
    hhttp 直接开个进程池就可以了,或者用 grequest 。基本都是开箱即用
    css3
        15
    css3  
    OP
       2021-03-30 13:43:48 +08:00 via iPhone
    @ch2 感谢老哥,学习了,我试试这个库。
    @hsfzxjy,我的理解是 with 自动会关闭呢,另外我也尝试把 yield 给放到 with 外边,也还是这个问题,应该主要问题出在多进程哪里,如同 13 扣老哥说的
    @shuax 嗯,多谢老哥
    @zonyitoo 学业不精,惭愧
    @renmu123 多谢,我试试
    aladdindingding
        16
    aladdindingding  
       2021-03-30 13:45:09 +08:00
    ulimit 改成 65535
    css3
        17
    css3  
    OP
       2021-03-30 14:11:53 +08:00
    @aladdindingding 本质是我进程和图片数一致了,这里有问题,改这个值,解决不了本质上的问题呢
    LeeReamond
        18
    LeeReamond  
       2021-03-30 14:33:27 +08:00 via Android
    你这个需求 py 的最佳实践应该是多线程 ffi 加异步 io,大概会比你现在的方案快很多很多很多很多
    laqow
        19
    laqow  
       2021-03-30 14:45:25 +08:00 via Android
    看起来是 2 楼的问题,yield 写 with 里面了,每次迭代文件都没关

    不改程序的话用 linux,想开多少文件开多少
    laqow
        20
    laqow  
       2021-03-30 14:52:41 +08:00 via Android
    感觉这样写每个图都是主进程开的,还没进 worker 就已经错误了
    css3
        21
    css3  
    OP
       2021-03-30 15:01:09 +08:00
    @laqow 我把 yield 放到 with 外,也是跑一半 OSError: [Errno 24] Too many open files, 放 linux 服务器上跑,跑的时间久点儿,最终也会 OSError: [Errno 24] Too many open files
    LeeReamond
        22
    LeeReamond  
       2021-03-30 15:26:10 +08:00 via Android   1
    @css3 跟 yield 没有关系,yield 只是起到保存状态中断执行的作用,你在循环里每次迭代,生成器也循环,with 管理器是正常结束的。另外仔细看了一下你的代码,你的多进程似乎仅负责网络通信,这是非常不合理的使用方法,建议了解 python 中的异步网络通信
    LeeReamond
        23
    LeeReamond  
       2021-03-30 15:34:10 +08:00 via Android
    @LeeReamond 你每新建进程,系统要开辟专门的文件指标指向输入输出流,而进程内部又为网络访问开辟了专门的文件。且 tcp 访问后有 timewait 状态,占用文件不会立即被释放,导致你的资源吃满。现代服务器单机每秒可以处理几十万个请求,即使用 python 也一样,绝不是你这仅仅一万个不现实请求能搞崩的。一个简单的多访问问题被你搞成这样。
    mjawp
        24
    mjawp  
       2021-03-30 15:41:26 +08:00
    这种业务需求应该是多线程比较好吧?

    1.多进程切换开销比多线程切换开销大
    2.合理的进程数应该是等于你的 cpu 核心数
    3.速度瓶颈主要还是等待请求和 IO,所以在等待 IO 与网络请求的时候可以切换很多很多不同的线程进行其他操作了
    imn1
        25
    imn1  
       2021-03-30 16:29:29 +08:00
    @liprais #2 说的是关键点
    不要在 with open 里面 yield
    yield 相当于生成器,数据是集中处理的,不是逐个处理,这就造成打开太多
    简单说就是全部 yield 的数据 都 获取后集中才处理,这时每个 open 都没有 close
    imn1
        26
    imn1  
       2021-03-30 17:11:04 +08:00
    前置重点:看下面第四点

    我做过类似的,不过不是 base64,而是 CRC32,应该比 base64 耗时,8K 张图片
    建议:
    1. base64 移出 os.walk,同时也建议 os.scandir 替换 walk,递归只 yield 返回路径就好了
    2. 多进程可以尝试换成 Pool+Pool.imap(),注意要用 close(),参考手册,Pool.close 要在 Pool.join 前面,同时限制线程数量
    3. 小问题,扩展名列表只有小写,你确保一万多文件扩展名都没有大写字母么?不小心会漏掉文件的
    4. 最后是严重的逻辑错误,img_to_base64 里面的 r 是个字典,你最后 return 一次就够了,怎么是不停 yield 这个字典呢?我觉得这是最大问题

    我以前考虑是遍历的同时处理文件,还是遍历了路径再处理文件,后来我看到遍历树是递归+yield,就不纠结了
    递归里面处理文件,处理文件 yield 结果,这两个都不是好想法,肯定有说不清的问题(因为 python 是调用系统 API 打开文件的),所以直接就用递归 yield 路径,然后再考虑其他方式优化文件处理
    imn1
        27
    imn1  
       2021-03-30 17:19:30 +08:00
    补充:你这样重复 yield 这个字典,里面还有 open,这样不是打开一万次(每文件一次),而是打开一万次的阶乘!!
    julyclyde
        28
    julyclyde  
       2021-03-30 19:29:07 +08:00
    字典还能 yield ??
    slidoooor
        29
    slidoooor  
       2021-03-30 23:26:47 +08:00
    学习了,感谢!
    itwhat
        30
    itwhat  
       2021-03-31 10:00:48 +08:00
    IO 密集型应考虑多线程
    css3
        31
    css3  
    OP
       2021-03-31 10:25:44 +08:00
    @itwhat 请教下,图片转 base64 仅仅是 IO 密集型吗?
    maybedk
        32
    maybedk  
       2021-03-31 18:43:37 +08:00
    r 那个字典没必要直接 yield (img,bs64),与 with 对齐;
    开进程池 pool = multiprocessing.Pool(4);
    基本能跑起来,速度多快不知道
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2645 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 39ms UTC 13:46 PVG 21:46 LAX 05:46 JFK 08:46
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86