在python中多次访问mmap对象

时间:2015-03-17 12:19:31

标签: python multithreading 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个副本(在我的情况下会占用太多内存)?

3 个答案:

答案 0 :(得分:1)

在另一个线程跳入并执行seek(0)之前,并不总是执行read()

  1. 说线程1执行读取,读取到文件末尾; seek(0)有 尚未执行。
  2. 然后线程2执行读取。 mmap中的文件指针仍然是 在文件的末尾。因此read()会返回''
  3. 错误检测代码已触发,因为content''
  4. 您可以使用切片来实现相同的结果,而不是使用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))
    .
    .
    .
    

    请注意,我已经改变了读取和搜索的顺序;首先进行搜索更有意义,将文件指针放在文件的开头。

答案 1 :(得分:1)

我无法看到你需要mmap开始的地方。 mmap是一种在进程之间共享数据的技术。你为什么不把内容读入内存(一次!),例如列表?然后每个线程将使用它自己的迭代器集访问列表。另外,请注意Python中的GIL,它可以防止使用多线程发生任何加速。如果你想要它,使用多处理(和然后一个mmaped文件是有意义的,但实际上是在各个进程之间共享)

答案 2 :(得分:0)

问题是线程之间共享单个mmap_object,以便线程A调用read,在它到达seek之前,线程B也调用read,因此没有数据。

你真正需要的是能够复制python mmap对象而不复制底层mmap,但我认为没办法做到这一点。

我认为,重写对象实现的唯一可行解决方案是每个mmap对象使用一个锁(互斥锁等)来阻止两个线程同时访问同一个对象。