多进程写文件时如何使用文件锁(fcntl 或 msvcrt)

发布时间 - 2026-01-23 00:00:00    点击率:
多进程写文件错乱的根本原因是write()系统调用在无同步机制下不保证原子性,尤其超PIPE_BUF时会被拆分;Linux/macOS用fcntl.flock加文件描述符级建议锁,Windows需用msvcrt.locking实现字节范围锁。

Python 多进程写文件时为什么直接 open + write 会出错

多个进程同时 open('log.txt', 'a') 并调用

write(),看似安全,实际常出现内容错乱、覆盖、甚至部分写入丢失。根本原因不是 Python 层面的 GIL(多进程绕过 GIL),而是底层系统调用 write() 在没有同步机制时,对同一文件描述符的并发操作不保证原子性——尤其当写入量超过 PIPE_BUF(通常 4KB)时,write() 可能被拆成多次系统调用,中间插入其他进程的写入。

Linux/macOS 下用 fcntl.flock 实现进程级文件锁

flock() 是最常用、语义清晰的方案,它基于内核维护的 advisory lock(建议锁),要求所有参与者主动调用加锁/解锁,不强制拦截非法写入,但足够可靠。

  • 锁是文件描述符粒度的,不是文件路径粒度:必须在 open() 后对返回的 fd 加锁,且子进程继承 fd 时锁状态可能变化(默认不继承,需设 FD_CLOEXEC=0 才可跨 fork 传递)
  • 使用 flock(fd, fcntl.LOCK_EX) 加排他锁,写完后 flock(fd, fcntl.LOCK_UN) 解锁;推荐配合 try/finally 或上下文管理器
  • 不要对已关闭的 fd 调用 flock(),会报 OSError: [Errno 9] Bad file descriptor
  • 示例片段:
    import fcntl
    import os
    

fd = os.open('data.txt', os.O_RDWR | os.O_CREAT) try: fcntl.flock(fd, fcntl.LOCK_EX) os.lseek(fd, 0, os.SEEK_END) os.write(fd, b'new line\n') finally: fcntl.flock(fd, fcntl.LOCK_UN) os.close(fd)

Windows 下必须用 msvcrt.locking 替代 flock

Windows 不支持 flock(),且其 open() 返回的文件对象不提供原生锁接口。唯一可移植的底层方案是 msvcrt.locking(),但它只作用于已打开的文件对象的字节区域,且仅限于普通磁盘文件(不支持管道、设备等)。

  • 必须用 os.open() 获取原始 fd,再用 os.fdopen(fd, 'r+b') 包装为文件对象,否则 msvcrt.locking() 会失败
  • 锁定范围需明确指定字节偏移和长度,例如锁定整个文件可传 0 偏移 + 1 字节长度(因 Windows 的 lock 是“字节范围锁”,最小单位是 1 字节,且锁任意一字节即阻塞对该文件全部写入)
  • 调用前确保文件已存在且可写,否则 locking()IOError: No locks available
  • 示例关键步骤:
    import os
    import msvcrt
    

fd = os.open('log.txt', os.O_RDWR | os.O_CREAT) try:

锁定第 0 字节(效果等同锁整个文件)

msvcrt.locking(fd, msvcrt.LK_NBLCK, 1)  # 非阻塞,失败抛异常
os.lseek(fd, 0, os.SEEK_END)
os.write(fd, b'win log\n')

finally: msvcrt.locking(fd, msvcrt.LK_UNLCK, 1) os.close(fd)

跨平台封装要注意的三个坑

真正落地时,不能简单 if-else 切换锁模块,还要处理:

  • fcntlmsvcrt 的异常类型不同:flock()OSErrorlocking()IOError(Py3 中已继承自 OSError,但仍需统一捕获)
  • Windows 下 LOCK_NB 对应 msvcrt.LK_NBLCK,但 Linux 的 LOCK_NB 是 flag,需与 LOCK_EX 按位或;而 Windows 是独立常量
  • 最容易被忽略的是:锁只对「通过该 fd 写入」起作用;如果某进程绕过锁、直接用 open(..., 'a').write(),锁完全无效——advisory lock 的本质就是靠自觉

文件锁不是银弹,它解决的是“多个进程协调写同一文件”的问题,而不是替代日志轮转、队列分发等更高层设计。真要高频写共享文件,优先考虑用 multiprocessing.Queue 或临时文件 + 原子重命名。


# linux  # python  # windows  # 字节  # mac  # ai  # macos  # win  # cos  # 同步机制  # 为什么 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: linux top下的 minerd 木马清除方法  Laravel如何实现用户注册和登录?(Auth脚手架指南)  如何确保西部建站助手FTP传输的安全性?  开心动漫网站制作软件下载,十分开心动画为何停播?  常州企业网站制作公司,全国继续教育网怎么登录?  百度输入法全感官ai怎么关 百度输入法全感官皮肤关闭  Python自然语言搜索引擎项目教程_倒排索引查询优化案例  Laravel Telescope怎么调试_使用Laravel Telescope进行应用监控与调试  黑客如何利用漏洞与弱口令入侵网站服务器?  如何破解联通资金短缺导致的基站建设难题?  html5怎么画眼睛_HT5用Canvas或SVG画眼球瞳孔加JS控制动态【绘制】  微信小程序 wx.uploadFile无法上传解决办法  Java遍历集合的三种方式  node.js报错:Cannot find module 'ejs'的解决办法  Laravel怎么做缓存_Laravel Cache系统提升应用速度的策略与技巧  Python结构化数据采集_字段抽取解析【教程】  Laravel如何实现一对一模型关联?(Eloquent示例)  奇安信“盘古石”团队突破 iOS 26.1 提权  如何用已有域名快速搭建网站?  Linux系统命令中screen命令详解  php在windows下怎么调试_phpwindows环境调试操作说明【操作】  香港服务器网站推广:SEO优化与外贸独立站搭建策略  Laravel如何使用Facades(门面)及其工作原理_Laravel门面模式与底层机制  Android利用动画实现背景逐渐变暗  怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?  北京网页设计制作网站有哪些,继续教育自动播放怎么设置?  如何快速重置建站主机并恢复默认配置?  佐糖AI抠图怎样调整抠图精度_佐糖AI精度调整与放大细化操作【攻略】  南京网站制作费用,南京远驱官方网站?  香港服务器WordPress建站指南:SEO优化与高效部署策略  儿童网站界面设计图片,中国少年儿童教育网站-怎么去注册?  javascript中的try catch异常捕获机制用法分析  厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?  中山网站制作网页,中山新生登记系统登记流程?  如何在景安服务器上快速搭建个人网站?  如何在 Telegram Web View(iOS)中防止键盘遮挡底部输入框  在线教育网站制作平台,山西立德教育官网?  如何有效防御Web建站篡改攻击?  JavaScript中如何操作剪贴板_ClipboardAPI怎么用  如何快速选择适合个人网站的云服务器配置?  如何快速上传建站程序避免常见错误?  Laravel如何使用.env文件管理环境变量?(最佳实践)  Laravel如何优雅地处理服务层_在Laravel中使用Service层和Repository层  Laravel如何实现登录错误次数限制_Laravel自带LoginThrottles限流配置【方法】  长沙企业网站制作哪家好,长沙水业集团官方网站?  Laravel如何集成第三方登录_Laravel Socialite实现微信QQ微博登录  想要更高端的建设网站,这些原则一定要坚持!  如何快速搭建高效WAP手机网站吸引移动用户?  高端建站三要素:定制模板、企业官网与响应式设计优化  制作无缝贴图网站有哪些,3dmax无缝贴图怎么调?