Skip to main content
 首页 » 编程设计

python之在 python 中多次访问 mmap 对象

2024年10月25日37leader

我有许多文件,映射到内存(作为 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. 假设线程 1 执行读取,读取到文件末尾; seek(0) 有 尚未执行。
  2. 然后线程 2 执行读取。 mmap中的文件指针仍然是 在文件的末尾。 read() 因此返回 ''。
  3. 错误检测代码被触发,因为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 中。然后在读取/查找之前调用 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)) 
. 
. 
. 

请注意,我已经更改了读取和查找的顺序;先查找更有意义,将文件指针定位在文件的开头。