免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 2910 | 回复: 10
打印 上一主题 下一主题

python中文件句柄的关闭问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2016-09-22 10:57 |只看该作者 |倒序浏览
父进程创建子进程,然后父子间使用multiprocessing.Queue()来进行通信。
首先子进程退出,关闭Queue,然后父进程也关闭Queue.

这时,使用命令lsof -c python3|egrep 'pipe|sem' ,会发现有pipe和sem似乎没被关闭,这是怎么回事?并且,每多创建一个子进程然后退出,就会有越来越多的pipe和sem句柄存留。求教怎么彻底关闭这些句柄,谢谢。
测试代码如下:
import os,sys,time,multiprocessing,queue,threading,signal
pdict = {}

def child(name,req):
        while True:
                ret = 'a'
                try:
                        ret = req.get(timeout=5)
                        print("%s get %s" %(name,ret))
                        time.sleep(10)
                        msg = "%s  exit now" %(name)
                        print(msg)
                        time.sleep(1)
                        req.close()
                        del req
                        os._exit(4)
                except Exception as e:
                        print("child err,%s" %str(e))

def start_child(name,req):
        print("to start %s now" %name)
        p = multiprocessing.Process(target=child, args=(name,req) )
        p.start()
        pdict[name] = [req,p]


def main():
        print(" main start now")
        req = ''
        count = 0       
        signal.signal(signal.SIGCHLD,signal.SIG_IGN)
        while True:
                count = 0
                while True:
                        count = count + 1
                        name = "child#%s" %count
                        req = multiprocessing.Queue()
               
                        print("\n")
                        print("start %s child now ......" %name)
                        p1 = threading.Thread( target=start_child,args = (name,req))                                                                                                                       
                        p1.start()

                        req.put([count])                       
                        time.sleep(13)                       
                               
                        rq = pdict[name][0]
                        p = pdict[name][1]
                        rq.close()
                        rq.close()
                        del rq
                        print("clear %s resource\n" %name)
                        time.sleep(20)
                        print(" begin the %s round now #################" %(count+1))                                       

if __name__ == "__main__":
        main()


随着子进程的创建退出,lsof显示如下:
第一个子进程创建后退出:
[root@Steve ~]# lsof -c python3|egrep 'pipe|sem'
python3 5135 root  DEL    REG   0,16          18198145 /dev/shm/sem.rBbhHD
python3 5135 root  DEL    REG   0,16          18198144 /dev/shm/sem.Z4yneZ
python3 5135 root  DEL    REG   0,16          18198143 /dev/shm/sem.DnUuLk
python3 5135 root    5r  FIFO    0,8      0t0 18198146 pipe


第二个子进程创建后退出:
[root@Steve ~]# lsof -c python3|egrep 'pipe|sem'
python3 5135 root  DEL    REG   0,16          18203718 /dev/shm/sem.1pq5ru
python3 5135 root  DEL    REG   0,16          18203717 /dev/shm/sem.B2iNRc
python3 5135 root  DEL    REG   0,16          18203716 /dev/shm/sem.xu6vhV
python3 5135 root  DEL    REG   0,16          18198145 /dev/shm/sem.rBbhHD
python3 5135 root  DEL    REG   0,16          18198144 /dev/shm/sem.Z4yneZ
python3 5135 root  DEL    REG   0,16          18198143 /dev/shm/sem.DnUuLk
python3 5135 root    5r  FIFO    0,8      0t0 18198146 pipe
python3 5135 root    6r  FIFO    0,8      0t0 18203719 pipe


第三个子进程创建后退出:
[root@Steve ~]# lsof -c python3|egrep 'pipe|sem'
python3 5135 root  DEL    REG   0,16          18209232 /dev/shm/sem.xhO5xe
python3 5135 root  DEL    REG   0,16          18209231 /dev/shm/sem.DXxJQj
python3 5135 root  DEL    REG   0,16          18209230 /dev/shm/sem.xHio9o
python3 5135 root  DEL    REG   0,16          18203718 /dev/shm/sem.1pq5ru
python3 5135 root  DEL    REG   0,16          18203717 /dev/shm/sem.B2iNRc
python3 5135 root  DEL    REG   0,16          18203716 /dev/shm/sem.xu6vhV
python3 5135 root  DEL    REG   0,16          18198145 /dev/shm/sem.rBbhHD
python3 5135 root  DEL    REG   0,16          18198144 /dev/shm/sem.Z4yneZ
python3 5135 root  DEL    REG   0,16          18198143 /dev/shm/sem.DnUuLk
python3 5135 root    5r  FIFO    0,8      0t0 18198146 pipe
python3 5135 root    6r  FIFO    0,8      0t0 18203719 pipe
python3 5135 root    7r  FIFO    0,8      0t0 18209233 pipe



论坛徽章:
0
2 [报告]
发表于 2016-09-22 15:04 |只看该作者
都没人吱一声啊

论坛徽章:
1
15-16赛季CBA联赛之新疆
日期:2017-03-09 12:33:45
3 [报告]
发表于 2016-09-23 08:39 |只看该作者
回复 1# qqrilxk

关键是这一句
  1. signal.signal(signal.SIGCHLD,signal.SIG_IGN)
复制代码
你要自己处理child退出的信号处理,如果处理了,就没有你说的问题了。
再提醒一下,你最好用代码标签来处理你的代码,这样人家看起来会舒服很多。

论坛徽章:
0
4 [报告]
发表于 2016-09-23 18:01 |只看该作者
谢谢楼上回复。
signal.signal(signal.SIGCHLD,signal.SIG_IGN)
这个默认忽略后,我在主进程后面对Queue有close操作啊。
如果注掉此行,然后在主进程后面wait一下,再对Queue进行close,现象是一样的。
还请大侠指点,谢谢。

论坛徽章:
1
15-16赛季CBA联赛之新疆
日期:2017-03-09 12:33:45
5 [报告]
发表于 2016-09-25 07:49 |只看该作者
本帖最后由 jeppeter 于 2016-09-25 13:01 编辑
  1. import os,sys,time,multiprocessing,queue,threading,signal
  2. pdict = {}

  3. def child(name,req):
  4.     while True:
  5.         ret = 'a'
  6.         try:
  7.             ret = req.get(timeout=5)
  8.             print("%s get %s" %(name,ret))
  9.             time.sleep(10)
  10.             msg = "%s  exit now" %(name)
  11.             print(msg)
  12.             time.sleep(1)
  13.             req.close()
  14.             del req
  15.             os._exit(4)
  16.         except Exception as e:
  17.             print("child err,%s" %str(e))

  18. def start_child(name,req):
  19.     print("to start %s now" %name)
  20.     p = multiprocessing.Process(target=child, args=(name,req) )
  21.     p.start()
  22.     pdict[name] = [req,p]


  23. def main():
  24.     print(" main start now")
  25.     req = ''
  26.     count = 0      
  27.     signal.signal(signal.SIGCHLD,signal.SIG_IGN)
  28.     while True:
  29.         count = 0
  30.         while True:
  31.             count = count + 1
  32.             name = "child#%s" %count
  33.             req = multiprocessing.Queue()
  34.             print("\n")
  35.             print("start %s child now ......" %name)
  36.             p1 = threading.Thread( target=start_child,args = (name,req))                                                                                                                       
  37.             p1.start()
  38.             req.put([count])                       
  39.             time.sleep(13)
  40.             rq = pdict[name][0]
  41.             p = pdict[name][1]
  42.             rq.close()
  43.             rq.close()
  44.             del rq
  45.             print("clear %s resource\n" %name)
  46.             pdict[name][0] = None
  47.             pdict[name][1] = None
  48.             time.sleep(20)
  49.             print(" begin the %s round now #################" %(count+1))                                       

  50. if __name__ == "__main__":
  51.     main()
复制代码

回复 4# qqrilxk

这里加了
  1.             pdict[name][0] = None
  2.             pdict[name][1] = None
复制代码
你以后要写代码,最好用格式化,要不你的代码看起来费劲。因为python是自动 回收,你原来的数据有数据无法删除。这样你的数据就可以了。

论坛徽章:
0
6 [报告]
发表于 2016-09-26 09:40 |只看该作者
谢谢楼上指点。
关于下面这两条代码,我以前试过了,我刚刚又试了一下,现象是一样的。还是存在句柄增加的问题。
  1. pdict[name][0] = None
  2. pdict[name][1] = None
复制代码
这个问题我花了很长时间偿试,还请大侠多费心帮帮,万分感谢。

另外关于代码格式,多谢指点,我以前真没太注意就直接贴上来了。

论坛徽章:
11
2015年迎新春徽章
日期:2015-03-04 09:55:282017金鸡报晓
日期:2017-02-08 10:39:4215-16赛季CBA联赛之辽宁
日期:2016-12-15 10:24:1715-16赛季CBA联赛之佛山
日期:2016-11-30 09:04:2015-16赛季CBA联赛之江苏
日期:2016-04-29 15:56:1215-16赛季CBA联赛之同曦
日期:2016-04-12 13:21:182016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之山东
日期:2016-02-16 11:37:52每日论坛发贴之星
日期:2016-02-07 06:20:00程序设计版块每日发帖之星
日期:2016-02-07 06:20:0015-16赛季CBA联赛之新疆
日期:2018-01-09 16:25:37
7 [报告]
发表于 2016-09-26 16:32 |只看该作者
44行是什么
            rq = pdict[name][0]
            p = pdict[name][1]
            rq.close()
            rq.close()
后面一个是p.close()吧

论坛徽章:
1
15-16赛季CBA联赛之新疆
日期:2017-03-09 12:33:45
8 [报告]
发表于 2016-09-27 09:35 |只看该作者
  1. import os,sys,time,multiprocessing,queue,threading,signal
  2. pdict = {}

  3. def child(name,req):
  4.     try:
  5.         ret = req.get(timeout=5)
  6.         print("%s get %s (%d)" %(name,ret,os.getpid()))
  7.         #time.sleep(1)
  8.         #req.close()
  9.         #del req
  10.     except Exception as e:
  11.         print("child err,%s" %str(e))
  12.     sys.exit(0)
  13.     return

  14. def start_child(name,req):
  15.     print("to start %s now" %name)
  16.     p = multiprocessing.Process(target=child, args=(name,req) )
  17.     p.start()
  18.     return p


  19. def main():
  20.     print(" main start now")
  21.     req = ''
  22.     count = 0      
  23.     #signal.signal(signal.SIGCHLD,signal.SIG_IGN)
  24.     count = 0
  25.     while True:
  26.         count = count + 1
  27.         name = "child#%s" %count
  28.         req = multiprocessing.Queue()
  29.         p = start_child(name,req)
  30.         req.put([count])
  31.         req.close()
  32.         del req
  33.         req = None
  34.         print("clear %s resource (%d)\n" %(name,p.pid))
  35.         #p.join()
  36.         del p
  37.         p = None
  38.         print("begin the %s round now #################" %(count+1))
  39.     return


  40. if __name__ == "__main__":
  41.     main()

复制代码


最关键的一句还是注释了
  1. #signal.signal(signal.SIGCHLD,signal.SIG_IGN)
复制代码

这样,multiprocessing 就会正确处理进程退出了,原来的处理,让multiprocessing不能正确处理。这一次是对了。

论坛徽章:
0
9 [报告]
发表于 2016-09-27 11:19 |只看该作者
这样处理似乎还是有点问题。
这样处理结果是:当程序运行到print("begin the %s round now #################" %(count+1))  这一行时,还是有句柄在打开的(在这行后增加time.sleep(10)可看到)。
这里对原程序的最大一个修改是删掉了全局变量pdict = {}对子进程信息的记录。这就使得每次运行到print("begin the %s round now #################" %(count+1))  这一行时,都只有相同数量的句柄在打开(没有句柄增加,可在这一行时应该是没有句柄才对),似乎规避了句柄增加的问题。
可我的问题是,我需要全局变量dict来记录子进程的信息(包括Queue和其它信息),在重启此子进程时释放Queue,因此这样做不太合本意。
从你上面的修改来看,如果不对子进程的Queue进行记录,在子进程后Queue的句柄就关闭了(回收了)。
但是,如果我们对其进行记录,为什么就不能通过close和del 来使得其关闭并回收呢?
到底需要怎样才能使得其得到关闭回收,这是我想知道的。下面是测试代码

  1. import os,sys,time,multiprocessing,queue,threading,signal
  2. pdict = {}
  3. count = 0

  4. def sigchld_handler(signum,frame):
  5.         os.waitpid(-1, os.WNOHANG)
  6.         name = "child#%s" %count
  7.         rq = pdict[name][0]
  8.         p = pdict[name][1]
  9.         rq.close()
  10.         rq.close()
  11.         del rq


  12. def child(name,req):
  13.         while True:
  14.                 ret = 'a'
  15.                 try:
  16.                         ret = req.get(timeout=5)
  17.                         print("%s get %s" %(name,ret))
  18.                         time.sleep(4)
  19.                         msg = "%s  exit now" %(name)
  20.                         print(msg)
  21.                         time.sleep(1)
  22.                         req.close()
  23.                         del req
  24.                         os._exit(4)
  25.                 except Exception as e:
  26.                         print("child err,%s" %str(e))

  27. def start_child(name,req):
  28.         print("to start %s now" %name)
  29.         p = multiprocessing.Process(target=child, args=(name,req) )
  30.         p.start()
  31.         pdict[name] = [req,p]


  32. def main():
  33.         print(" main start now")
  34.         req = ''
  35.                
  36.         signal.signal(signal.SIGCHLD,signal.SIG_IGN)
  37.         #signal( SIGCHLD, sigchld_handler );
  38.         while True:
  39.                 count = 0
  40.                 while True:
  41.                         count = count + 1
  42.                         name = "child#%s" %count
  43.                         req = multiprocessing.Queue()
  44.                
  45.                         print("\n")
  46.                         print("start %s child now ......" %name)
  47.                         p1 = threading.Thread( target=start_child,args = (name,req))                                                                                                                       
  48.                         p1.start()

  49.                         req.put([count])                       
  50.                         time.sleep(6)                       
  51.                
  52.                         pdict[name][0].close()
  53.                         del pdict[name][1]
  54.                         del pdict[name][0]
  55.                         print("clear %s resource\n" %name)
  56.                         #pdict[name][0] = None
  57.                         #pdict[name][1] = None
  58.                         pdict[name] = None
  59.                         time.sleep(10)
  60.                         print(" begin the %s round now #################" %(count+1))                                       

  61. if __name__ == "__main__":
  62.         main()
复制代码

论坛徽章:
1
15-16赛季CBA联赛之新疆
日期:2017-03-09 12:33:45
10 [报告]
发表于 2016-09-27 15:55 |只看该作者
  1. import os,sys,time,multiprocessing,queue,threading,signal
  2. pdict = {}

  3. def child(name,req):
  4.     try:
  5.         ret = req.get(timeout=5)
  6.         print("%s get %s (%d)" %(name,ret,os.getpid()))
  7.         #time.sleep(1)
  8.         #req.close()
  9.         #del req
  10.     except Exception as e:
  11.         print("child err,%s" %str(e))
  12.     sys.exit(0)
  13.     return

  14. def start_child(name,req):
  15.     print("to start %s now" %name)
  16.     p = multiprocessing.Process(target=child, args=(name,req) )
  17.     p.start()
  18.     return p


  19. def main():
  20.     print(" main start now")
  21.     req = ''
  22.     count = 0      
  23.     #signal.signal(signal.SIGCHLD,signal.SIG_IGN)
  24.     count = 0
  25.     while True:
  26.         count = count + 1
  27.         name = "child#%s" %count
  28.         req = multiprocessing.Queue()
  29.         p = start_child(name,req)
  30.         pdict[name] = [req,p]
  31.         req.put([count])
  32.         pdict.pop(name,None)
  33.         req.close()
  34.         del req
  35.         req = None
  36.         print("clear %s resource (%d)\n" %(name,p.pid))
  37.         #p.join()
  38.         del p
  39.         p = None
  40.         print("begin the %s round now #################" %(count+1))
  41.     return


  42. if __name__ == "__main__":
  43.     main()

复制代码

回复 9# qqrilxk

这里就实现了你的功能,至于你说的,不是立刻删除,这个是PYTHON内部实现的机制,是一种LAZY-MODE的方式,不是一出现垃圾就回收,而是等一会儿,垃圾比较多了,再一起回收的问题,只要运行超过1000个以上的实例,没有出现大幅的增加FILE就可以了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP