跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • python3 多进程求助 OSError: [Errno 24] Too many open files
未分類
31 3 月 2021

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

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

資深大佬 : css3 0

业务:有大量(本次测试时 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)

  • 資深大佬 : BrettD

    ulimit 不行吗

  • 資深大佬 : liprais

    yield 那里缩进不对

  • 資深大佬 : CRVV

    1. 发 HTTP 请求不需要用多进程
    2. 如果在乎性能,请用 requests.session
    3. 如果单线程顺序发请求不够快,可以用 ThreadPoolExecutor 或者 aiohttp

  • 資深大佬 : sss495088732

    做个缓冲区….要限制一下并行的进程,句柄数…你这能跑到一半儿,那电脑也是非常不戳

  • 資深大佬 : UN2758

    你定义的 img_to_base64 是单纯的生成器,想要协程支持的生成器,得给它加上 @asyncio.coroutine

  • 資深大佬 : UN2758

    @UN2758 #5 看错

  • 資深大佬 : xiaolinjia

    开了 1w 多进程牛的,整个 multiprocessing.Pool 进程池限制下并行数吧。

  • 主 資深大佬 : css3

    @BrettD mac 8192 跑满了

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

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

    @sss495088732 不知道咋搞缓冲区

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

    @xiaolinjia 好的,多谢,我找下文档

  • 資深大佬 : ch2

    1 万个任务正常做法是用进程池开 n 个进程(n<=你的 cpu 核心数*2)
    分批陆续完成,而不是 1 万个进程一拥而上
    而且你的这个代码瓶颈在网速上,根本不需要多进程,用多线程就能处理
    但是最优的做法是用 grequests 一次批量发 1000 个请求,配合上错误重试,分 10-20 次就搞定了

  • 資深大佬 : ch2

    随便找到网图,grequests 是编程最简单的大批量发 http 请求的方法,几乎没有唯二的其他选择,你会用 requests 就会用 grequests
    python3 多进程求助 OSError: [Errno 24] Too many open files

  • 資深大佬 : hsfzxjy

    @css3 你要等文件关了再 yield

  • 資深大佬 : shuax

    开 100 个进程差不多了,再多也不快。

  • 資深大佬 : zonyitoo

    每个图标单独开一个进程去跑,这居然能跑得动电脑的配置也着实厉害了

  • 資深大佬 : renmu123

    hhttp 直接开个进程池就可以了,或者用 grequest 。基本都是开箱即用

  • 主 資深大佬 : css3

    @ch2 感谢老哥,学习了,我试试这个库。
    @hsfzxjy,我的理解是 with 自动会关闭呢,另外我也尝试把 yield 给放到 with 外边,也还是这个问题,应该主要问题出在多进程哪里,如同 13 扣老哥说的
    @shuax 嗯,多谢老哥
    @zonyitoo 学业不精,惭愧
    @renmu123 多谢,我试试

  • 資深大佬 : aladdindingding

    ulimit 改成 65535

  • 主 資深大佬 : css3

    @aladdindingding 本质是我进程和图片数一致了,这里有问题,改这个值,解决不了本质上的问题呢

  • 資深大佬 : LeeReamond

    你这个需求 py 的最佳实践应该是多线程 ffi 加异步 io,大概会比你现在的方案快很多很多很多很多

  • 資深大佬 : laqow

    看起来是 2 的问题,yield 写 with 里面了,每次迭代文件都没关

    不改程序的话用 linux,想开多少文件开多少

  • 資深大佬 : laqow

    感觉这样写每个图都是主进程开的,还没进 worker 就已经错误了

  • 主 資深大佬 : css3

    @laqow 我把 yield 放到 with 外,也是跑一半 OSError: [Errno 24] Too many open files, 放 linux 服务器上跑,跑的时间久点儿,最终也会 OSError: [Errno 24] Too many open files

  • 資深大佬 : LeeReamond

    @css3 跟 yield 没有关系,yield 只是起到保存状态中断执行的作用,你在循环里每次迭代,生成器也循环,with 管理器是正常结束的。另外仔细看了一下你的代码,你的多进程似乎仅负责网络通信,这是非常不合理的使用方法,建议了解 python 中的异步网络通信

  • 資深大佬 : LeeReamond

    @LeeReamond 你每新建进程,系统要开辟专门的文件指标指向输入输出流,而进程内部又为网络访问开辟了专门的文件。且 tcp 访问后有 timewait 状态,占用文件不会立即被释放,导致你的资源吃满。现代服务器单机每秒可以处理几十万个请求,即使用 python 也一样,绝不是你这仅仅一万个不现实请求能搞崩的。一个简单的多访问问题被你搞成这样。

  • 資深大佬 : mjawp

    这种业务需求应该是多线程比较好吧?

    1.多进程切换开销比多线程切换开销大
    2.合理的进程数应该是等于你的 cpu 核心数
    3.速度瓶颈主要还是等待请求和 IO,所以在等待 IO 与网络请求的时候可以切换很多很多不同的线程进行其他操作了

  • 資深大佬 : imn1

    @liprais #2 说的是关键点
    不要在 with open 里面 yield
    yield 相当于生成器,数据是集中处理的,不是逐个处理,这就造成打开太多
    简单说就是全部 yield 的数据 都 获取后集中才处理,这时每个 open 都没有 close

  • 資深大佬 : imn1

    前置重点:看下面第四点

    我做过类似的,不过不是 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

    补充:你这样重复 yield 这个字典,里面还有 open,这样不是打开一万次(每文件一次),而是打开一万次的阶乘!!

  • 資深大佬 : julyclyde

    字典还能 yield ??

  • 資深大佬 : slidoooor

    学习了,感谢!

  • 資深大佬 : itwhat

    IO 密集型应考虑多线程

  • 主 資深大佬 : css3

    @itwhat 请教下,图片转 base64 仅仅是 IO 密集型吗?

  • 資深大佬 : maybedk

    r 那个字典没必要直接 yield (img,bs64),与 with 对齐;
    开进程池 pool = multiprocessing.Pool(4);
    基本能跑起来,速度多快不知道

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具