Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/rust/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 多处理:在进程之间共享大型只读对象?_Python_Multiprocessing - Fatal编程技术网

Python 多处理:在进程之间共享大型只读对象?

Python 多处理:在进程之间共享大型只读对象?,python,multiprocessing,Python,Multiprocessing,子进程是否通过共享程序中先前创建的对象生成 我有以下设置: do_some_processing(filename): for line in file(filename): if line.split(',')[0] in big_lookup_object: # something here if __name__ == '__main__': big_lookup_object = marshal.load('file.bin')

子进程是否通过共享程序中先前创建的对象生成

我有以下设置:

do_some_processing(filename):
    for line in file(filename):
        if line.split(',')[0] in big_lookup_object:
            # something here

if __name__ == '__main__':
    big_lookup_object = marshal.load('file.bin')
    pool = Pool(processes=4)
    print pool.map(do_some_processing, glob.glob('*.data'))
我正在将一些大对象加载到内存中,然后创建一个需要使用该大对象的工作人员池。大对象是只读访问的,我不需要在进程之间传递对它的修改

我的问题是:大对象是否像我在unix/c中生成一个进程那样加载到共享内存中,还是每个进程都加载自己的大对象副本

更新:进一步澄清-大查找对象是一个共享查找对象。我不需要把它分开单独处理。我需要保留一份副本。我需要拆分它的工作是读取大量其他大文件,并根据查找对象查找这些大文件中的项

进一步更新:数据库是一个很好的解决方案,memcached可能是一个更好的解决方案,而磁盘上的文件(shelve或dbm)可能更好。在这个问题上,我对内存解决方案特别感兴趣。对于最终的解决方案,我将使用hadoop,但我想看看是否也可以使用本地内存版本

通过多处理生成的子进程是否共享程序中先前创建的对象

否(python 3.8之前的版本),以及

进程有独立的内存空间

解决方案1

要充分利用一个有大量工人的大型结构,请这样做

  • 将每个工作进程写入“筛选器”-从
    stdin
    读取中间结果,执行操作,在
    stdout
    上写入中间结果

  • 将所有工人连接为管道:

    process1 <source | process2 | process3 | ... | processn >result
    
    process1结果
    
  • 每个进程读、做和写

    这是非常有效的,因为所有进程都是并发运行的。写入和读取直接通过进程之间的共享缓冲区传递


    解决方案2

    在某些情况下,您有一个更复杂的结构-通常是扇出结构。在这种情况下,您有一个具有多个子项的父项

  • 父项打开源数据。父对象派生出许多子对象

  • 父级读取源,将源的一部分转发给每个并发运行的子级

  • 当父对象到达终点时,关闭管道。子项获取文件结尾并正常完成

  • 子部分很容易编写,因为每个子部分只需读取
    sys.stdin

    父母在繁殖所有的孩子和正确地保留管道方面有一点花哨的步法,但这并不太糟糕

    扇入是相反的结构。许多独立运行的进程需要将它们的输入交织到一个公共进程中。收集器并不容易编写,因为它必须从许多源读取

    通常使用
    select
    模块读取许多命名管道,以查看哪些管道具有挂起的输入


    解决方案3

    共享查找是数据库的定义

    解决方案3A–加载数据库。让工作人员处理数据库中的数据

    解决方案3B–创建一个非常简单的服务器,使用(或类似)提供响应HTTP GET的WSGI应用程序,以便工作人员可以查询服务器


    解决方案4

    共享文件系统对象。Unix操作系统提供共享内存对象。这些只是映射到内存的文件,以便交换I/O,而不是进行更多约定缓冲读取

    可以通过多种方式从Python上下文中执行此操作

  • 编写一个启动程序,(1)将原始的巨大对象分解为较小的对象,(2)启动工人,每个工人都有一个较小的对象。较小的对象可以是pickle-Python对象,以节省少量的文件读取时间

  • 编写一个启动程序,该程序(1)读取原始巨型对象,并使用
    seek
    操作编写一个页面结构的字节编码文件,以确保通过简单的seek可以轻松找到各个部分。这就是数据库引擎所做的–将数据分解为多个页面,通过
    搜索
    轻松定位每个页面


  • 生成具有此大型页面结构文件访问权限的工作人员。每个工作人员都可以找到相关部件并在那里工作。

    不同的进程有不同的地址空间。比如运行解释器的不同实例。这就是IPC(进程间通信)的用途

    为此,您可以使用队列或管道。如果以后要通过网络分发进程,也可以使用tcp上的rpc


    如果您在Unix下运行,它们可能共享同一对象,原因是(即子进程有单独的内存,但它是写时复制的,因此只要没有人修改它,就可以共享)。我尝试了以下方法:

    import multiprocessing
    
    x = 23
    
    def printx(y):
        print x, id(x)
        print y
    
    if __name__ == '__main__':
        pool = multiprocessing.Pool(processes=4)
        pool.map(printx, (1,2,3,4))
    
    并得到以下输出:

    $ ./mtest.py 23 22995656 1 23 22995656 2 23 22995656 3 23 22995656 4 美元/公吨/年 23 22995656 1. 23 22995656 2. 23 22995656 3. 23 22995656 4. 当然,这并不能证明没有制作副本,但在您的情况下,您应该能够通过查看
    ps
    的输出来验证这一点,以查看每个子进程使用了多少实际内存。

    是正确的。Python的多处理快捷方式有效地为您提供了一个单独的、重复的内存块

    在大多数*nix系统上,使用对
    os.fork()
    的较低级别调用实际上会给您提供写时拷贝内存,这可能就是您的想法。好吧,理论上,在最简单的程序中,你可以从数据中读取数据而不需要复制

    然而,在Python解释器中事情并不是那么简单。对象数据和元数据存储在同一内存段中,因此即使对象从未更改,该对象的引用计数器之类的内容也会增加,从而导致内存写入
    #!/usr/bin/env python
    import os, sys, time
    from multiprocessing import Pool
    
    x = 23000 # replace `23` due to small integers share representation
    z = []    # integers are immutable, let's try mutable object
    
    def printx(y):
        global x
        if y == 3:
           x = -x
        z.append(y)
        print os.getpid(), x, id(x), z, id(z) 
        print y
        if len(sys.argv) == 2 and sys.argv[1] == "sleep":
           time.sleep(.1) # should make more apparant the effect
    
    if __name__ == '__main__':
        pool = Pool(processes=4)
        pool.map(printx, (1,2,3,4))
    
    $ python26 test_share.py sleep
    2504 23000 11639492 [1] 10774408
    1
    2564 23000 11639492 [2] 10774408
    2
    2504 -23000 11639384 [1, 3] 10774408
    3
    4084 23000 11639492 [4] 10774408
    4
    
    $ python26 test_share.py
    1148 23000 11639492 [1] 10774408
    1
    1148 23000 11639492 [1, 2] 10774408
    2
    1148 -23000 11639324 [1, 2, 3] 10774408
    3
    1148 -23000 11639324 [1, 2, 3, 4] 10774408
    4
    
    import time
    import multiprocessing
    
    def load_data( queue_load, n_processes )
    
        ... load data here into some_variable
    
        """
        Store multiple copies of the data into
        the data queue. There needs to be enough
        copies available for each process to access. 
        """
    
        for i in range(n_processes):
            queue_load.put(some_variable)
    
    
    def work_with_data( queue_data, queue_load ):
    
        # Wait for load_data() to complete
        while queue_load.empty():
            time.sleep(1)
    
        some_variable = queue_load.get()
    
        """
        ! Tuples can also be used here
        if you have multiple data files
        you wish to keep seperate.  
        a,b = queue_load.get()
        """
    
        ... do some stuff, resulting in new_data
    
        # store it in the queue
        queue_data.put(new_data)
    
    
    def start_multiprocess():
    
        n_processes = 5
    
        processes = []
        stored_data = []
    
        # Create two Queues
        queue_load = multiprocessing.Queue()
        queue_data = multiprocessing.Queue()
    
        for i in range(n_processes):
    
            if i == 0:
                # Your big data file will be loaded here...
                p = multiprocessing.Process(target = load_data,
                args=(queue_load, n_processes))
    
                processes.append(p)
                p.start()   
    
            # ... and then it will be used here with each process
            p = multiprocessing.Process(target = work_with_data,
            args=(queue_data, queue_load))
    
            processes.append(p)
            p.start()
    
        for i in range(n_processes)
            new_data = queue_data.get()
            stored_data.append(new_data)    
    
        for p in processes:
            p.join()
        print(processes)