千家信息网

Python怎么实现父子进程共享文件对象

发表于:2025-11-15 作者:千家信息网编辑
千家信息网最后更新 2025年11月15日,这篇文章主要介绍了Python怎么实现父子进程共享文件对象的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Python怎么实现父子进程共享文件对象文章都会有所收获,下面我们
千家信息网最后更新 2025年11月15日Python怎么实现父子进程共享文件对象

这篇文章主要介绍了Python怎么实现父子进程共享文件对象的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Python怎么实现父子进程共享文件对象文章都会有所收获,下面我们一起来看看吧。

直接上代码:

from multiprocessing import Process, Lock

err_file = 'error1.log'

err_fd = open(err_file, 'w')

def put(fd):

print "PUT"

fd.write("hello, func put writen")

print "END"

if __name__=='__main__':

p_list=[]

for i in range(1):

p_list.append(Process(target=put, args=(err_fd,)))

for p in p_list:

p.start()

for p in p_list:

p.join()

上面的代码意图很清晰: 通过multiprocessing.Process派生一个进程, 去执行put函数, put函数的作用也是很清楚, 输出PUT和END, 并且将"hello, func put write" 写到文件error1.log中.

那么按理说, 输出应该如同上面说的那样, PUT和END,然后error1.log将有那句话"hello, func put write", 然而, 世事总有那么点难料的, 代码执行结果是:

[root@iZ23pynfq19Z ~]# py27 2.py ; cat error1.log

PUT

END

[root@iZ23pynfq19Z ~]#

what!? 为什么error1.log没东西 !?

让我们稍微调整下代码, 再见证神奇的事情:

from multiprocessing import Process, Lock

err_file = 'error1.log'

err_fd = open(err_file, 'w')

def put(fd):

print "PUT"

fd.write("hello, func put writen")

fd.write("o" * 4075) # 神奇的一行

print "END"

if __name__=='__main__':

p_list=[]

for i in range(1):

p_list.append(Process(target=put, args=(err_fd,))) for p in p_list:

p.start()

for p in p_list:

p.join()

输出结果:

[root@iZ23pynfq19Z ~]# py27 2.py ; cat error1.log

PUT

END

hello, func put write

o....(有4075个)

[root@iZ23pynfq19Z ~]#

有没有觉得一种懵逼的感觉!?

如今, 心中涌现两个问题:

  1. 为什么第一个程序无法写入那句话 , 但是第二个却可以?

  2. 那个4075是什么鬼?

在解释这些问题之前, 我们需要清楚标准IO库所具有的特点: 全缓冲, 行缓冲, 不缓冲

具体可以看之前博文:https://my.oschina.net/u/2291453/blog/806102

因为现在是写入文件, 所以系统IO将采用全缓冲的方式, 也就是说, 会将缓冲区填满才刷入系统写队列.

所以上面的问题就一下子全解决了, 正因为那些 迷一般的 'o',填满了整个缓冲区, 所以系统将我们的内容刷进去写队列,所以4075怎么来, 就是用4096-sizeof("hello, func put writen")+1, 为什么要+1, 因为缓冲区满还不行, 要大于才能触发写动作.

所以我们现在已经能够得出答案, 如果我们想要在multiprcessing.Process中, 用上面类似的方式去写文件时,有三种方法去实现:

  1. 写满缓冲区

  2. 手动调用flush()

  3. 将文件对象设置成不缓冲

第一第二种在上面已经阐述, 那我们简单讲下第三种:

取自Python官网Document:

open(name[, mode[, buffering]])

...

The optional buffering argument specifies the file's desired buffer size: 0 means unbuffered,

1 means line buffered, any other positive value means use a buffer of (approximately) that

size (in bytes). A negative buffering means to use the system default, which is usually line

buffered for tty devices and fully buffered for other files. If omitted, the system default is

used. [2]

上图说明就是, 允许我们在open的时候, 设置buffering为0, 那么就是unbuffered模式, 那么在每次写, 就是直接写入写队列,而不是写到缓冲区.(性能最低的方式)

----------------我是切割线----------------

谈论完现象和处理的方法, 我们应该来点深入的;

相信我们曾经试过, 在没有显示关闭文件对象或者显示调用flush时, 文件依旧能够正常写入,那么又是怎么一回事呢?

其实,在我们正常关闭程序时, 进程在退出将会为我们做一些"手尾", 例如关闭打开的文件描述符, 清理临时文件,清理内存等等.正是因为系统的这种"好习惯", 所以我们的数据在文件描述符关闭时,就能刷入写队列,文件内容也不会丢失.

那么基于这种认识,我们再回首刚才的问题, 在子进程调用put的时候, 理论上在程序退出时, 并没显示关闭文件描述符, 所以数据在缓冲区就丢失了.

让我们在顺藤摸瓜,看Process的实现

multiprocessing/Processing.py

def start(self):

'''

Start child process

'''

assert self._popen is None, 'cannot start a process twice'

assert self._parent_pid == os.getpid(),

'can only start a process object created by current process'

assert not _current_process._daemonic,

'daemonic processes are not allowed to have children'

_cleanup()

if self._Popen is not None:

Popen = self._Popen

else:

from .forking import Popen

self._popen = Popen(self)

_current_process._children.add(self)

再看下Popn是怎么做?

multiprocessing/forking.py

class Popen(object):

def __init__(self, process_obj):

sys.stdout.flush()

sys.stderr.flush()

self.returncode = None

self.pid = os.fork()

if self.pid == 0:

if 'random' in sys.modules:

import random

random.seed()

code = process_obj._bootstrap()

sys.stdout.flush()

sys.stderr.flush()

os._exit(code)

关键地方就是最后的 os._exit(code), 为什么说最关键? 因为这部分的退出, 将决定进程会处理什么"手尾",

os._exit是什么鬼? 其实就是标准库的_eixt, 于是我们又能简单学习这东西了

https://my.oschina.net/u/2291453/blog/813259

在上面的链接, 我们能够比较清楚看到 _exit() 和exit() 是比较不同的两个东西, _exit() 简单暴力, 直接丢弃用户态的内容,进入内核, 而exit()则比较耐心地为我们清理

那么我们是否能够假设: 如果Popen的退出不是os._exit() 会是怎样的效果呢?厦门叉车租赁公司

很幸运的是, sys.exit() 就是我们先要的exit(), 事不宜迟, 赶紧试下!

multiprocessing/forking.py

class Popen(object):

def __init__(self, process_obj):

sys.stdout.flush()

sys.stderr.flush()

self.returncode = None

self.pid = os.fork()

if self.pid == 0:

if 'random' in sys.modules:

import random

random.seed()

code = process_obj._bootstrap()

sys.stdout.flush()

sys.stderr.flush()

#os._exit(code)

sys.exit(code)

测试代码, 返回最原始那个没有'o'填充的版本

[root@iZ23pynfq19Z ~]# python 2.py ; cat error1.log

PUT

END

hello, func put write

我们可以看到, 确实是可以写进去, 这样就证明上面的说法是站得住脚步的

关于"Python怎么实现父子进程共享文件对象"这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对"Python怎么实现父子进程共享文件对象"知识都有一定的了解,大家如果还想学习更多知识,欢迎关注行业资讯频道。

文件 缓冲 进程 就是 对象 缓冲区 代码 内容 父子 系统 问题 队列 面的 清楚 东西 方式 知识 程序 输出 神奇 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 王者荣耀夏哥的服务器在哪 数据库中建立两个表的联系 计算机软件开发份额证书 MT数据库技术流视频 sql数据库基本操作 江苏常规网络技术咨询产品 阿里云服务器杀毒软件 服务器无法在当前安全上下文 闵行区方便软件开发供应商 软件开发实现文件关键字检索 高端科技创新互联网 数据库服务器常用端口 软件开发文档有什么好处 genbank数据库网址 网络安全招聘学历 全面提升网络安全和信息化 乡镇网络安全工作重大事项研究 阿里云数据库读写分离 监控及服务器密码管理制度 软件开发类技术方案 服务器机房管理服务方案 武汉电脑软件开发平台 崇礼软件开发设计在线咨询 网络技术类学科 内部服务器日常管理 饥荒怎么购买专用服务器 网络安全领导小组组织机构 怎样查询数据库是否修改 兰州免费的外贸数据库客户信息 网络安全和信息化一体之两翼
0