簡體   English   中英

如何在Python中使用類實例的多處理?

[英]How to use multiprocessing with class instances in Python?

我試圖創建一個類,而不是可以運行一個單獨的進程去做一些需要很長時間的工作,從主模塊中啟動一堆這些,然后等待它們全部完成。 我想一次啟動流程,然后繼續為他們提供待辦事項,而不是創建和銷毀流程。 例如,也許我有10個運行dd命令的服務器,然后我希望它們全部scp文件等。

我的最終目標是為每個系統創建一個類,用於跟蹤與其綁定的系統的信息,如IP地址,日志,運行時等。但該類必須能夠啟動系統命令然后返回在系統命令運行時執行回執行調用程序,以便稍后跟隨系統命令的結果。

我的嘗試失敗了,因為我無法通過pickle將管道上的類的實例方法發送到子進程。 那些不是pickleable。 因此,我試圖以各種方式解決它,但我無法弄明白。 如何修補我的代碼呢? 如果你不能發送任何有用的東西,多處理有什么用?

是否有與類實例一起使用的多處理的良好文檔? 我可以使多處理模塊工作的唯一方法是簡單的功能。 在類實例中使用它的每次嘗試都失敗了。 也許我應該通過事件呢? 我還不明白該怎么做。

import multiprocessing
import sys
import re

class ProcessWorker(multiprocessing.Process):
    """
    This class runs as a separate process to execute worker's commands in parallel
    Once launched, it remains running, monitoring the task queue, until "None" is sent
    """

    def __init__(self, task_q, result_q):
        multiprocessing.Process.__init__(self)
        self.task_q = task_q
        self.result_q = result_q
        return

    def run(self):
        """
        Overloaded function provided by multiprocessing.Process.  Called upon start() signal
        """
        proc_name = self.name
        print '%s: Launched' % (proc_name)
        while True:
            next_task_list = self.task_q.get()
            if next_task is None:
                # Poison pill means shutdown
                print '%s: Exiting' % (proc_name)
                self.task_q.task_done()
                break
            next_task = next_task_list[0]
            print '%s: %s' % (proc_name, next_task)
            args = next_task_list[1]
            kwargs = next_task_list[2]
            answer = next_task(*args, **kwargs)
            self.task_q.task_done()
            self.result_q.put(answer)
        return
# End of ProcessWorker class

class Worker(object):
    """
    Launches a child process to run commands from derived classes in separate processes,
    which sit and listen for something to do
    This base class is called by each derived worker
    """
    def __init__(self, config, index=None):
        self.config = config
        self.index = index

        # Launce the ProcessWorker for anything that has an index value
        if self.index is not None:
            self.task_q = multiprocessing.JoinableQueue()
            self.result_q = multiprocessing.Queue()

            self.process_worker = ProcessWorker(self.task_q, self.result_q)
            self.process_worker.start()
            print "Got here"
            # Process should be running and listening for functions to execute
        return

    def enqueue_process(target):  # No self, since it is a decorator
        """
        Used to place an command target from this class object into the task_q
        NOTE: Any function decorated with this must use fetch_results() to get the
        target task's result value
        """
        def wrapper(self, *args, **kwargs):
            self.task_q.put([target, args, kwargs]) # FAIL: target is a class instance method and can't be pickled!
        return wrapper

    def fetch_results(self):
        """
        After all processes have been spawned by multiple modules, this command
        is called on each one to retreive the results of the call.
        This blocks until the execution of the item in the queue is complete
        """
        self.task_q.join()                          # Wait for it to to finish
        return self.result_q.get()                  # Return the result

    @enqueue_process
    def run_long_command(self, command):
        print "I am running number % as process "%number, self.name

        # In here, I will launch a subprocess to run a  long-running system command
        # p = Popen(command), etc
        # p.wait(), etc
        return 

    def close(self):
        self.task_q.put(None)
        self.task_q.join()

if __name__ == '__main__':
    config = ["some value", "something else"]
    index = 7
    workers = []
    for i in range(5):
        worker = Worker(config, index)
        worker.run_long_command("ls /")
        workers.append(worker)
    for worker in workers:
        worker.fetch_results()

    # Do more work... (this would actually be done in a distributor in another class)

    for worker in workers:
        worker.close() 

編輯:我試圖移動ProcessWorker類並在Worker類之外創建多處理隊列,然后嘗試手動pickle工作器實例。 即使這不起作用,我得到一個錯誤

RuntimeError:只應通過繼承在進程之間共享隊列對象

但我只是將這些隊列的引用傳遞給worker實例? 我遺漏了一些基本的東西。 以下是主要部分的修改代碼:

if __name__ == '__main__':
    config = ["some value", "something else"]
    index = 7
    workers = []
    for i in range(1):
        task_q = multiprocessing.JoinableQueue()
        result_q = multiprocessing.Queue()
        process_worker = ProcessWorker(task_q, result_q)
        worker = Worker(config, index, process_worker, task_q, result_q)
        something_to_look_at = pickle.dumps(worker) # FAIL:  Doesn't like queues??
        process_worker.start()
        worker.run_long_command("ls /")

所以,問題在於我假設Python正在做某種與C ++ / fork()工作方式不同的魔術。 我不知何故認為Python只復制了類,而不是將整個程序復制到一個單獨的進程中。 我嚴重浪費了幾天試圖讓這個工作,因為所有關於pickle序列化的討論使我認為它實際上發送了所有東西通過管道。 我知道某些東西不能通過管道發送,但我認為我的問題是我沒有正確包裝。

如果Python文檔給了我一個10,000英尺的視圖,了解使用此模塊時會發生什么,這一切都可以避免。 當然,它告訴我多進程模塊的方法是做什么的,並給我一些基本的例子,但我想知道的是幕后的“操作理論”是什么! 這是我可以使用的那種信息。 如果我的回答沒有,請發信息。 它會幫助我學習。

當您使用此模塊運行啟動流程時,整個程序將復制到另一個流程中。 但是因為它不是“ __main__ ”進程而我的代碼正在檢查它,所以它不會無限啟動另一個進程。 它只是停下來,坐在那里等待做某事,比如一個僵屍。 調用multiprocess.Process()時在父級中初始化的所有內容都已設置完畢並准備就緒。 一旦你把東西放在multiprocess.Queue或共享內存,或管道等(但你正在溝通),然后單獨的進程接收它並開始工作。 它可以在所有導入的模塊和設置上繪制,就像它是父項一樣。 但是,一旦某些內部狀態變量在父進程或單獨進程中發生更改,則這些更改將被隔離。 一旦生成了該進程,現在就可以通過隊列,管道,共享內存等方式在必要時使它們保持同步。

我拋棄了代碼並重新開始,但現在我只在ProcessWorker中添加了一個額外的功能,一個運行命令行的“執行”方法。 很簡單。 我不必擔心以這種方式啟動然后關閉一堆進程,這在過去的C ++中引起了各種不穩定性和性能問題。 當我在開始時切換到啟動進程然后將消息傳遞給那些等待進程時,我的性能得到了改善並且非常穩定。

順便說一句,我查看了這個鏈接以獲得幫助,這讓我失望,因為這個例子讓我覺得方法是通過隊列傳輸的: http//www.doughellmann.com/PyMOTW/multiprocessing/communication.html第二個例子第一部分使用“next_task()”出現(對我來說)執行通過隊列接收的任務。

嘗試發送要執行的方法的名稱 ,而不是嘗試發送方法本身(這是不切實際的)。

如果每個worker都運行相同的代碼,那就是一個簡單的getattr(self, task_name)

我傳遞元組(task_name, task_args) ,其中task_args是一個直接輸入任務方法的字典:

next_task_name, next_task_args = self.task_q.get()
if next_task_name:
  task = getattr(self, next_task_name)
  answer = task(**next_task_args)
  ...
else:
  # poison pill, shut down
  break

REF: https//stackoverflow.com/a/14179779

大衛林奇1月6日6:03的回答當他說他被http://www.doughellmann.com/PyMOTW/multiprocessing/communication.html誤導時,事實並非正確。

提供的代碼和示例是正確的,並按照廣告的方式工作。 next_task() 正在執行通過隊列接收的任務 - 嘗試並理解Task.__call__()方法正在做什么。

在我的情況下,絆倒我的是run()實現中的語法錯誤。 似乎子流程不會報告這個並且只是默默地失敗 - 讓事情陷入奇怪的循環! 確保在Emacs中運行某種語法檢查程序,例如Flymake / Pyflakes。

通過multiprocessing.log_to_stderr()調試F幫助我縮小了問題的范圍。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM