【问题标题】:Multiple access to mmap objects in pythonpython中对mmap对象的多次访问
【发布时间】:2015-03-17 12:19:31
【问题描述】:

我有许多文件,映射到内存(作为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 个副本(在我的情况下会消耗太多内存)之外,有没有办法绕过这个问题?

【问题讨论】:

    标签: python multithreading mmap


    【解决方案1】:

    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))
    .
    .
    .
    

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

    【讨论】:

    • 我明白了,会发生什么。问题是:如何避免?
    • @mhawke,为什么切片可以提供更好的性能?谷歌快速搜索了几下,几乎一无所获。
    【解决方案2】:

    我看不到您需要从哪里开始 mmap。 mmap 是一种在进程之间共享数据的技术。为什么不直接将内容读入内存(一次!),例如作为列表?然后每个线程将使用它自己的一组迭代器访问列表。另外,请注意 Python 中的 GIL,它可以防止使用多线程进行任何加速。如果您愿意,请使用多处理(然后一个映射文件是有意义的,但实际上是在各个进程之间共享的)

    【讨论】:

    • mmap 除了在进程之间共享之外还有其他一些优点,例如高效随机访问操作系统管理分页的大文件。
    • 感谢您提及 GIL。然而,由于我的实际线程中的外部应用程序调用,我确实获得了所需的加速。
    • @mhawke 啊,有趣。
    【解决方案3】:

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

    您真正需要的是能够在不复制底层 mmap 的情况下复制 python mmap 对象,但我认为没有办法做到这一点。

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

    【讨论】:

      猜你喜欢
      • 2021-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-12
      • 2021-11-23
      相关资源
      最近更新 更多