LoginSignup
4
4

More than 5 years have passed since last update.

即席multiprocessing

Last updated at Posted at 2015-04-02

(4/5追記) Pickleした一時ファイルで受け渡すパターン
(9/4追記) Windows対応版

デコレータを作っておいて、既存の関数を書き直さずに子プロセスに処理を投げます。

test.py

import os
from multiprocessing import Process, Queue

# デコレータ
def mp(func):
    def _f(*args, **kwargs):
        def _p(*args, **kwargs):
            al = list(args)
            q = al.pop()
            q.put(func(*al, **kwargs))
        queue = Queue()
        argsl = list(args)
        argsl.append(queue)
        proc = Process(target=_p, args=argsl, kwargs=kwargs)
        proc.start()
        proc.join()
        return queue.get()
    return _f


# 子プロセスで実行して結果を返す
@mp
def mp_test(a, b=None):
    return (a + b, os.getpid())

result, child = mp_test(1, 2)
print("parent: {}".format(os.getpid()))
print("child: {}".format(child)
print(result)

(4/5追記) Pickleした一時ファイルで受け渡すパターン
データサイズが大きい場合

test2.py
import os
import pickle
from multiprocessing import Process

# デコレータ
def pickle_file(func):
    def _f(*args, **kwargs):
        def _p(*args, **kwargs):
            al = list(args)
            n = al.pop()
            res = func(*al, **kwargs)
            with open(n, "wb") as f:
                pickle.dump(res, f)
        tmp_name = "_tmp.pickle"
        argsl = list(args)
        argsl.append(tmp_name)
        proc = Process(target=_p, args=argsl, kwargs=kwargs)
        proc.start()
        proc.join()
        with open(tmp_name, "rb") as f:
            result = pickle.load(f)
        os.remove(tmp_name)
        return result
    return _f

# 子プロセスで実行して結果を返す
@pickle_file
def mp_test(a, b=None):
    return (a + b, os.getpid())

result, child = mp_test(1, 2)
print("parent: {}".format(os.getpid()))
print("child: {}".format(child)
print(result)

(9/4追記) Windows対応版
Windowsではプロセス開始方式がspawnなので、targetで渡す関数がpickle化できなければなりません。ネストした関数はpickle化できないので、この手法は使えません。

Windowsの場合は何の面白みも無いですがクラスを作成します。

test_win.py

import os
import pickle
import multiprocessing as mp


class FileBasedIPC(object):
    def __init__(self, func):
        self.func = func

    def _f(self, *args, **kwargs):
        tmp_name = "_tmp.pickle"
        argsl = list(args)
        argsl.append(tmp_name)
        proc = mp.Process(target=self._p, args=argsl, kwargs=kwargs)
        proc.start()
        proc.join()
        with open(tmp_name, "rb") as f:
            result = pickle.load(f)
        os.remove(tmp_name)
        return result

    def _p(self, *args, **kwargs):
        al = list(args)
        n = al.pop()
        res = self.func(*al, **kwargs)
        with open(n, "wb") as f:
            pickle.dump(res, f)

    def __call__(self):
        return self._f

# 子プロセスで実行して結果を返す
def mp_test(a, b=None):
    return (a + b, os.getpid())

result, child = FileBasedIPC(mp_test)()(1, 2)
print("parent: {}".format(os.getpid()))
print("child: {}".format(child)
print(result)
4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4