【问题标题】:Python : multiprocessing and Array of c_char_pPython:多处理和 c_char_p 数组
【发布时间】:2013-01-08 14:36:42
【问题描述】:

我正在启动 3 个进程,我希望它们将一个字符串放入一个共享数组中,在对应于进程 (i) 的索引处。

看下面的代码,生成的输出是:

['test 0', None, None]
['test 1', 'test 1', None]
['test 2', 'test 2', 'test 2']

为什么“测试 0”会被 test 1 覆盖,而 test 1 会被 test 2 覆盖?

我想要的是(顺序不重要):

['test 0', None, None]
['test 0', 'test 1', None]
['test 0', 'test 1', 'test 2']

代码:

#!/usr/bin/env python

import multiprocessing
from multiprocessing import Value, Lock, Process, Array
import ctypes
from ctypes import c_int, c_char_p

class Consumer(multiprocessing.Process):
    def __init__(self, task_queue, result_queue, arr, lock):
            multiprocessing.Process.__init__(self)
            self.task_queue = task_queue
            self.result_queue = result_queue
            self.arr = arr
            self.lock = lock

    def run(self):
            proc_name = self.name
            while True:
                next_task = self.task_queue.get()
                if next_task is None:
                    self.task_queue.task_done()
                    break            
                answer = next_task(arr=self.arr, lock=self.lock)
                self.task_queue.task_done()
                self.result_queue.put(answer)
            return

class Task(object):
    def __init__(self, i):
        self.i = i

    def __call__(self, arr=None, lock=None):
        with lock:
            arr[self.i] = "test %d" % self.i
            print arr[:]

    def __str__(self):
        return 'ARC'

    def run(self):
        print 'IN'

if __name__ == '__main__':
   tasks = multiprocessing.JoinableQueue()
   results = multiprocessing.Queue()

   arr = Array(ctypes.c_char_p, 3)

   lock = multiprocessing.Lock()

   num_consumers = multiprocessing.cpu_count() * 2
   consumers = [Consumer(tasks, results, arr, lock) for i in xrange(num_consumers)]

   for w in consumers:
      w.start()

   for i in xrange(3):
      tasks.put(Task(i))

   for i in xrange(num_consumers):
      tasks.put(None)

我正在运行 Python 2.7.3 (Ubuntu)

【问题讨论】:

    标签: python multiprocessing ctypes


    【解决方案1】:

    这个问题似乎类似于this one。在那里,J.F. Sebastian 推测对arr[i] 的分配指向arr[i] 的内存地址,该地址仅对进行分配的子进程有意义。其他子进程在查看该地址时会检索垃圾。

    至少有两种方法可以避免这个问题。一种是使用multiprocessing.manager 列表:

    import multiprocessing as mp
    
    class Consumer(mp.Process):
        def __init__(self, task_queue, result_queue, lock, lst):
                mp.Process.__init__(self)
                self.task_queue = task_queue
                self.result_queue = result_queue
                self.lock = lock
                self.lst = lst
    
        def run(self):
                proc_name = self.name
                while True:
                    next_task = self.task_queue.get()
                    if next_task is None:
                        self.task_queue.task_done()
                        break            
                    answer = next_task(lock = self.lock, lst = self.lst)
                    self.task_queue.task_done()
                    self.result_queue.put(answer)
                return
    
    class Task(object):
        def __init__(self, i):
            self.i = i
    
        def __call__(self, lock, lst):
            with lock:
                lst[self.i] = "test {}".format(self.i)
                print([lst[i] for i in range(3)])
    
    if __name__ == '__main__':
       tasks = mp.JoinableQueue()
       results = mp.Queue()
       manager = mp.Manager()
       lst = manager.list(['']*3)
    
       lock = mp.Lock()
       num_consumers = mp.cpu_count() * 2
       consumers = [Consumer(tasks, results, lock, lst) for i in xrange(num_consumers)]
    
       for w in consumers:
          w.start()
    
       for i in xrange(3):
          tasks.put(Task(i))
    
       for i in xrange(num_consumers):
          tasks.put(None)
    
       tasks.join()
    

    另一种方法是使用固定大小的共享数组,例如mp.Array('c', 10)

    import multiprocessing as mp
    
    class Consumer(mp.Process):
        def __init__(self, task_queue, result_queue, arr, lock):
                mp.Process.__init__(self)
                self.task_queue = task_queue
                self.result_queue = result_queue
                self.arr = arr
                self.lock = lock
    
        def run(self):
                proc_name = self.name
                while True:
                    next_task = self.task_queue.get()
                    if next_task is None:
                        self.task_queue.task_done()
                        break            
                    answer = next_task(arr = self.arr, lock = self.lock)
                    self.task_queue.task_done()
                    self.result_queue.put(answer)
                return
    
    class Task(object):
        def __init__(self, i):
            self.i = i
    
        def __call__(self, arr, lock):
            with lock:
                arr[self.i].value = "test {}".format(self.i)
                print([a.value for a in arr])
    
    if __name__ == '__main__':
       tasks = mp.JoinableQueue()
       results = mp.Queue()
       arr = [mp.Array('c', 10) for i in range(3)]
    
       lock = mp.Lock()
       num_consumers = mp.cpu_count() * 2
       consumers = [Consumer(tasks, results, arr, lock) for i in xrange(num_consumers)]
    
       for w in consumers:
          w.start()
    
       for i in xrange(3):
          tasks.put(Task(i))
    
       for i in xrange(num_consumers):
          tasks.put(None)
    
       tasks.join()
    

    我推测,当mp.Array(ctypes.c_char_p, 3) 不起作用时,这是因为mp.Array('c', 10) 的大小是固定的,所以内存地址永远不会改变,而mp.Array(ctypes.c_char_p, 3) 的大小是可变的,所以内存地址可能会改变arr[i] 分配给更大的字符串。

    也许这就是the docs 所警告的内容,

    虽然可以将指针存储在共享内存中,但请记住 这将引用特定地址空间中的位置 过程。但是,指针很可能在 第二个进程的上下文并尝试从 第二个进程可能会导致崩溃。

    【讨论】:

    • 谢谢你十亿次!您的两个解决方案都确实有效:) 我遇到了 J.F. Sebastian 的那篇文章,但由于某种原因无法实现它...... doh !现在你告诉我我应该在哪里建造你的雕像!?再次感谢...
    • 感谢您提出有趣的问题和您的热情!希望在 Stackoverflow 上见到你更多。至于雕像——我认为单击复选标记上方的向上箭头会非常棒;^)
    • 我会尽快获得所需的 15 名声望,我不会忘记 ;)
    • @unutbu - 我也是一尊雕像;)+1 谢谢不错的回答。
    猜你喜欢
    • 2019-03-26
    • 2019-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-24
    • 2014-11-20
    相关资源
    最近更新 更多