为什么我的文件在 Python 中从多个进程写入时没有损坏?
Why isn't my file corrupted while writing to it from multiple processes in Python?
很明显,如果 write()
调用未以某种方式同步,则从多个进程写入同一文件可能会导致数据损坏。请参阅另一个问题:Python multiprocessing safely writing to a file.
但是,在出于测试目的尝试重现此可能的错误时,我无法导致文件消息混淆。我想这样做是为了有效地比较有锁和没有锁的安全性。
什么都不做,文件似乎受到了某种保护。
import multiprocessing
import random
NUM_WORKERS = 10
LINE_SIZE = 10000
NUM_LINES = 10000
def writer(i):
line = ("%d " % i) * LINE_SIZE + "\n"
with open("file.txt", "a") as file:
for _ in range(NUM_LINES):
file.write(line)
def check(file):
for _ in range(NUM_LINES * NUM_WORKERS):
values = next(file).strip().split()
assert len(values) == LINE_SIZE
assert len(set(values)) == 1
if __name__ == "__main__":
processes = []
for i in range(NUM_WORKERS):
process = multiprocessing.Process(target=writer, args=(i, ))
processes.append(process)
for process in processes:
process.start()
for process in processes:
process.join()
with open("file.txt", "r") as file:
check(file)
我正在使用 Linux 并且我也知道文件写入可能是原子的,具体取决于缓冲区大小:Is file append atomic in UNIX?.
我试图增加消息的大小,但它没有产生损坏的数据。
您是否知道我可以使用在 Linux 上使用多处理生成损坏文件的任何代码示例?
AFAIU,锁定是由内核完成的。即使您没有要求,您也会看到锁定效果的原因是默认情况下 O_NONBLOCK
文件状态标志未设置(我猜是在打开文件时)。
请参阅手册中有关 文件状态标志 的部分,尤其是 operating modes 和 man 2 fcntl
。
我因此修补了您的示例以查看 O_NONBLOCK
的效果(事实上,断言现在确实失败了):
--- 1.py.orig 2019-07-05 14:49:13.276289018 +0300
+++ 1.py 2019-07-05 14:51:11.674727731 +0300
@@ -1,5 +1,7 @@
import multiprocessing
import random
+import os
+import fcntl
NUM_WORKERS = 10
LINE_SIZE = 10000
@@ -8,6 +10,8 @@
def writer(i):
line = ("%d " % i) * LINE_SIZE + "\n"
with open("file.txt", "a") as file:
+ flag = fcntl.fcntl(file.fileno(), fcntl.F_GETFD)
+ fcntl.fcntl(file.fileno(), fcntl.F_SETFL, flag | os.O_NONBLOCK)
for _ in range(NUM_LINES):
file.write(line)
来源:参见 and (and/or man 3p write
).
很明显,如果 write()
调用未以某种方式同步,则从多个进程写入同一文件可能会导致数据损坏。请参阅另一个问题:Python multiprocessing safely writing to a file.
但是,在出于测试目的尝试重现此可能的错误时,我无法导致文件消息混淆。我想这样做是为了有效地比较有锁和没有锁的安全性。
什么都不做,文件似乎受到了某种保护。
import multiprocessing
import random
NUM_WORKERS = 10
LINE_SIZE = 10000
NUM_LINES = 10000
def writer(i):
line = ("%d " % i) * LINE_SIZE + "\n"
with open("file.txt", "a") as file:
for _ in range(NUM_LINES):
file.write(line)
def check(file):
for _ in range(NUM_LINES * NUM_WORKERS):
values = next(file).strip().split()
assert len(values) == LINE_SIZE
assert len(set(values)) == 1
if __name__ == "__main__":
processes = []
for i in range(NUM_WORKERS):
process = multiprocessing.Process(target=writer, args=(i, ))
processes.append(process)
for process in processes:
process.start()
for process in processes:
process.join()
with open("file.txt", "r") as file:
check(file)
我正在使用 Linux 并且我也知道文件写入可能是原子的,具体取决于缓冲区大小:Is file append atomic in UNIX?.
我试图增加消息的大小,但它没有产生损坏的数据。
您是否知道我可以使用在 Linux 上使用多处理生成损坏文件的任何代码示例?
AFAIU,锁定是由内核完成的。即使您没有要求,您也会看到锁定效果的原因是默认情况下 O_NONBLOCK
文件状态标志未设置(我猜是在打开文件时)。
请参阅手册中有关 文件状态标志 的部分,尤其是 operating modes 和 man 2 fcntl
。
我因此修补了您的示例以查看 O_NONBLOCK
的效果(事实上,断言现在确实失败了):
--- 1.py.orig 2019-07-05 14:49:13.276289018 +0300
+++ 1.py 2019-07-05 14:51:11.674727731 +0300
@@ -1,5 +1,7 @@
import multiprocessing
import random
+import os
+import fcntl
NUM_WORKERS = 10
LINE_SIZE = 10000
@@ -8,6 +10,8 @@
def writer(i):
line = ("%d " % i) * LINE_SIZE + "\n"
with open("file.txt", "a") as file:
+ flag = fcntl.fcntl(file.fileno(), fcntl.F_GETFD)
+ fcntl.fcntl(file.fileno(), fcntl.F_SETFL, flag | os.O_NONBLOCK)
for _ in range(NUM_LINES):
file.write(line)
来源:参见man 3p write
).