多次访问 python 中的 mmap 对象
Multiple access to mmap objects in python
我有许多文件映射到内存(作为 mmap
个对象)。在处理过程中,每个文件都必须打开多次。如果只有一个线程,它工作正常。但是,当我尝试运行并行执行任务时,出现了一个问题:不同的线程不能同时访问同一个文件。此示例说明了该问题:
import mmap, threading
class MmapReading(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
for i in range(10000):
content = mmap_object.read().decode('utf-8')
mmap_object.seek(0)
if not content:
print('Error while reading mmap object')
with open('my_dummy_file.txt', 'w') as f:
f.write('Hello world')
with open('my_dummy_file.txt', 'r') as f:
mmap_object = mmap.mmap(f.fileno(), 0, prot = mmap.PROT_READ)
threads = []
for i in range(64):
threads.append(MmapReading())
threads[i].daemon = True
threads[i].start()
for thread in threading.enumerate():
if thread != threading.current_thread():
thread.join()
print('Mmap reading testing done!')
每当我 运行 这个脚本时,我都会收到大约 20 条错误消息。
除了为每个文件制作 64 个副本(在我的情况下这会消耗太多内存)之外,有没有办法避免这个问题?
seek(0)
并不总是在另一个线程跳入并执行 read()
之前执行。
- 假设线程 1 执行读取,读取到文件末尾;
seek(0)
有
尚未执行。
- 然后线程2执行读取。 mmap中的文件指针仍然是
在文件的末尾。
read()
因此 returns ''
.
- 因为
content
是''
,所以触发了错误检测代码。
不使用 read()
,您可以使用切片来获得相同的结果。替换:
content = mmap_object.read().decode('utf-8')
mmap_object.seek(0)
和
content = mmap_object[:].decode('utf8')
content = mmap_object[:mmap_object.size()]
也有效。
锁定是另一种方式,但在这种情况下没有必要。如果你想尝试一下,你可以使用全局 threading.Lock
对象并在实例化时将其传递给 MmapReading
。将锁对象存储在实例变量 self.lock
中。然后在reading/seeking之前调用self.lock.acquire()
,之后调用self.lock.release()
。执行此操作时您会遇到非常明显的性能损失。
from threading import Lock
class MmapReading(threading.Thread):
def __init__(self, lock):
self.lock = lock
threading.Thread.__init__(self)
def run(self):
for i in range(10000):
self.lock.acquire()
mmap_object.seek(0)
content = mmap_object.read().decode('utf-8')
self.lock.release()
if not content:
print('Error while reading mmap object')
lock = Lock()
for i in range(64):
threads.append(MmapReading(lock))
.
.
.
请注意,我已经更改了读取和查找的顺序;先查找更有意义,将文件指针定位在文件的开头。
我看不出你需要 mmap 从哪里开始。 mmap 是一种在进程 之间共享数据 的技术。你为什么不把内容读入内存(一次!)例如作为列表?然后每个线程将使用它自己的一组迭代器访问列表。另外,请注意 Python 中的 GIL,它可以防止使用多线程进行任何加速。如果需要,请使用多处理(然后 然后 映射文件有意义,但实际上在各个进程之间共享)
问题是单个 mmap_object 正在线程之间共享,因此线程 A 调用 read 并且在它到达查找之前,线程 B 也调用 read,因此没有数据。
您真正需要的是能够在不复制底层 mmap 的情况下复制 python mmap 对象,但我认为没有办法做到这一点。
我认为除了重写对象实现之外唯一可行的解决方案是为每个 mmap 对象使用一个锁(互斥锁等)以防止两个线程同时访问同一个对象。
我有许多文件映射到内存(作为 mmap
个对象)。在处理过程中,每个文件都必须打开多次。如果只有一个线程,它工作正常。但是,当我尝试运行并行执行任务时,出现了一个问题:不同的线程不能同时访问同一个文件。此示例说明了该问题:
import mmap, threading
class MmapReading(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
for i in range(10000):
content = mmap_object.read().decode('utf-8')
mmap_object.seek(0)
if not content:
print('Error while reading mmap object')
with open('my_dummy_file.txt', 'w') as f:
f.write('Hello world')
with open('my_dummy_file.txt', 'r') as f:
mmap_object = mmap.mmap(f.fileno(), 0, prot = mmap.PROT_READ)
threads = []
for i in range(64):
threads.append(MmapReading())
threads[i].daemon = True
threads[i].start()
for thread in threading.enumerate():
if thread != threading.current_thread():
thread.join()
print('Mmap reading testing done!')
每当我 运行 这个脚本时,我都会收到大约 20 条错误消息。
除了为每个文件制作 64 个副本(在我的情况下这会消耗太多内存)之外,有没有办法避免这个问题?
seek(0)
并不总是在另一个线程跳入并执行 read()
之前执行。
- 假设线程 1 执行读取,读取到文件末尾;
seek(0)
有 尚未执行。 - 然后线程2执行读取。 mmap中的文件指针仍然是
在文件的末尾。
read()
因此 returns''
. - 因为
content
是''
,所以触发了错误检测代码。
不使用 read()
,您可以使用切片来获得相同的结果。替换:
content = mmap_object.read().decode('utf-8')
mmap_object.seek(0)
和
content = mmap_object[:].decode('utf8')
content = mmap_object[:mmap_object.size()]
也有效。
锁定是另一种方式,但在这种情况下没有必要。如果你想尝试一下,你可以使用全局 threading.Lock
对象并在实例化时将其传递给 MmapReading
。将锁对象存储在实例变量 self.lock
中。然后在reading/seeking之前调用self.lock.acquire()
,之后调用self.lock.release()
。执行此操作时您会遇到非常明显的性能损失。
from threading import Lock
class MmapReading(threading.Thread):
def __init__(self, lock):
self.lock = lock
threading.Thread.__init__(self)
def run(self):
for i in range(10000):
self.lock.acquire()
mmap_object.seek(0)
content = mmap_object.read().decode('utf-8')
self.lock.release()
if not content:
print('Error while reading mmap object')
lock = Lock()
for i in range(64):
threads.append(MmapReading(lock))
.
.
.
请注意,我已经更改了读取和查找的顺序;先查找更有意义,将文件指针定位在文件的开头。
我看不出你需要 mmap 从哪里开始。 mmap 是一种在进程 之间共享数据 的技术。你为什么不把内容读入内存(一次!)例如作为列表?然后每个线程将使用它自己的一组迭代器访问列表。另外,请注意 Python 中的 GIL,它可以防止使用多线程进行任何加速。如果需要,请使用多处理(然后 然后 映射文件有意义,但实际上在各个进程之间共享)
问题是单个 mmap_object 正在线程之间共享,因此线程 A 调用 read 并且在它到达查找之前,线程 B 也调用 read,因此没有数据。
您真正需要的是能够在不复制底层 mmap 的情况下复制 python mmap 对象,但我认为没有办法做到这一点。
我认为除了重写对象实现之外唯一可行的解决方案是为每个 mmap 对象使用一个锁(互斥锁等)以防止两个线程同时访问同一个对象。