Багатопроцесорна робота: Як використовувати Pool.map для функції, визначеної у класі?


179

Коли я запускаю щось на кшталт:

from multiprocessing import Pool

p = Pool(5)
def f(x):
     return x*x

p.map(f, [1,2,3])

це чудово працює. Однак, ставлячи це як функцію класу:

class calculate(object):
    def run(self):
        def f(x):
            return x*x

        p = Pool()
        return p.map(f, [1,2,3])

cl = calculate()
print cl.run()

Надає мені таку помилку:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/sw/lib/python2.6/threading.py", line 532, in __bootstrap_inner
    self.run()
  File "/sw/lib/python2.6/threading.py", line 484, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/sw/lib/python2.6/multiprocessing/pool.py", line 225, in _handle_tasks
    put(task)
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

Я бачив повідомлення від Алекса Мартеллі, що займається тим же видом проблеми, але це було недостатньо явно.


1
"це як функція класу"? Чи можете ви опублікувати код, який фактично отримує фактичну помилку. Без фактичного коду ми можемо лише здогадуватися, що ти робиш неправильно.
С.Лотт

Як загальне зауваження, існують модулі маринування, більш потужні, ніж стандартний модуль соління Python (на зразок модуля вигулу, згаданого у цій відповіді ).
klaus se

1
У мене була схожа проблема із закриттями IPython.Parallel, але там ви могли подолати проблему, натиснувши об'єкти на вузли. Здається, це дуже прикро подолати цю проблему з багатопроцесорною роботою.
Alex S

Тут calculateможна вибрати, тому, схоже, це можна вирішити за допомогою 1) створення об'єкта функції з конструктором, який копіює calculateекземпляр, а потім 2) передачі екземпляра цього об'єкта функції до методу Pools map. Немає?
rd11

1
@math Я не вірю, що будь-яка з останніх змін Python не допоможе. Деякі обмеження multiprocessingмодуля зумовлені його метою бути кросплатформою, а також відсутністю fork(2)подібного системного виклику в Windows. Якщо ви не переймаєтесь підтримкою Win32, можливо, існує більш просте рішення на основі процесу. Або , якщо ви готові використовувати потоки замість процесів, ви можете замінити from multiprocessing import Poolз from multiprocessing.pool import ThreadPool as Pool.
Айя

Відповіді:


69

Мене також дратували обмеження щодо того, які функції pool.map можуть прийняти. Я написав наступне, щоб обійти це. Схоже, це працює навіть при рекурсивному використанні parmap.

from multiprocessing import Process, Pipe
from itertools import izip

def spawn(f):
    def fun(pipe, x):
        pipe.send(f(x))
        pipe.close()
    return fun

def parmap(f, X):
    pipe = [Pipe() for x in X]
    proc = [Process(target=spawn(f), args=(c, x)) for x, (p, c) in izip(X, pipe)]
    [p.start() for p in proc]
    [p.join() for p in proc]
    return [p.recv() for (p, c) in pipe]

if __name__ == '__main__':
    print parmap(lambda x: x**x, range(1, 5))

1
Це дуже добре спрацювало для мене, дякую. Я виявив одну слабку сторону: я спробував використовувати parmap для деяких функцій, які пройшли навколо дефолту, і знову отримав PicklingError. Я не знайшов рішення для цього, я просто переробив свій код, щоб не використовувати засудження за замовчуванням.
sans

2
Це не працює в Python 2.7.2 (за замовчуванням, 12 червня 2011, 15:08:59) [MSC v.1500 32 біт (Intel)] на win32
ubershmekel

3
Це працює на Python 2.7.3 1,2012, 05:14:39. Це не працює на гігантських ітерабелях -> це викликає OSError: [Errno 24] Занадто багато відкритих файлів через кількість відкритих труб.
Eiyrioü von Kauyf

Це рішення породжує процес для кожного робочого елемента. Рішення "klaus se" нижче є більш ефективним.
ypnos

85

Я не міг використовувати коди, опубліковані поки що, оскільки коди, що використовують "multiprocessing.Pool", не працюють з лямбда-виразами, а коди, що не використовують "multiprocessing.Pool", породжують стільки процесів, скільки є робочих елементів.

Я адаптував код, коли він породжує заздалегідь задану кількість працівників і лише повторюється через список вводу, якщо існує непрацюючий працівник. Я також включив режим "демон" для робітників st ctrl-c працює, як очікувалося.

import multiprocessing


def fun(f, q_in, q_out):
    while True:
        i, x = q_in.get()
        if i is None:
            break
        q_out.put((i, f(x)))


def parmap(f, X, nprocs=multiprocessing.cpu_count()):
    q_in = multiprocessing.Queue(1)
    q_out = multiprocessing.Queue()

    proc = [multiprocessing.Process(target=fun, args=(f, q_in, q_out))
            for _ in range(nprocs)]
    for p in proc:
        p.daemon = True
        p.start()

    sent = [q_in.put((i, x)) for i, x in enumerate(X)]
    [q_in.put((None, None)) for _ in range(nprocs)]
    res = [q_out.get() for _ in range(len(sent))]

    [p.join() for p in proc]

    return [x for i, x in sorted(res)]


if __name__ == '__main__':
    print(parmap(lambda i: i * 2, [1, 2, 3, 4, 6, 7, 8]))

2
Як би ви отримали смужку прогресу, щоб правильно працювати з цією parmapфункцією?
ударник

2
Питання - я використав це рішення, але зауважив, що пітонні процеси, які я породив, залишалися активними в пам'яті. Будь-яка швидка думка про те, як їх убити, коли ваша припустка виходить?
CompEcon

1
@ klaus-se Я знаю, що ми не відмовляємося від того, щоб просто сказати подяки в коментарях, але твоя відповідь для мене занадто цінна, я не міг протистояти. Я хотів би, щоб я міг тобі дати більше, ніж одну репутацію ...
deshtop

2
@greole, що передається (None, None)як останній елемент, вказує на funте, що він досяг кінця послідовності елементів для кожного процесу.
aganders3

4
@deshtop: Ви можете з великою репутацією, якщо у вас достатньо репутації :-)
Марк

57

Багатопроцесорна та маринована робота порушена і обмежена, якщо ви не переходите за межі стандартної бібліотеки.

Якщо ви використовуєте вилку multiprocessingвиклику pathos.multiprocesssing, ви можете безпосередньо використовувати класи та методи класів у багатопроцесорнихmap функціях . Це тому dill, що використовується замість pickleабо cPickle, і dillможе серіалізувати майже що завгодно в python.

pathos.multiprocessingтакож надає функцію асинхронної карти ... і вона може mapфункціонувати з декількома аргументами (наприклад map(math.pow, [1,2,3], [4,5,6]))

Дивіться дискусії: Що може робити багатопроцесорний і кроповий разом?

і: http://matthewrocklin.com/blog/work/2013/12/05/Parallelism-and-Serialization

Він навіть обробляє код, який ви написали спочатку, без змін та від перекладача. Навіщо робити щось інше, що є більш крихким і характерним для одного випадку?

>>> from pathos.multiprocessing import ProcessingPool as Pool
>>> class calculate(object):
...  def run(self):
...   def f(x):
...    return x*x
...   p = Pool()
...   return p.map(f, [1,2,3])
... 
>>> cl = calculate()
>>> print cl.run()
[1, 4, 9]

Отримати код тут: https://github.com/uqfoundation/pathos

І, просто показати трохи більше того, що він може зробити:

>>> from pathos.multiprocessing import ProcessingPool as Pool
>>> 
>>> p = Pool(4)
>>> 
>>> def add(x,y):
...   return x+y
... 
>>> x = [0,1,2,3]
>>> y = [4,5,6,7]
>>> 
>>> p.map(add, x, y)
[4, 6, 8, 10]
>>> 
>>> class Test(object):
...   def plus(self, x, y): 
...     return x+y
... 
>>> t = Test()
>>> 
>>> p.map(Test.plus, [t]*4, x, y)
[4, 6, 8, 10]
>>> 
>>> res = p.amap(t.plus, x, y)
>>> res.get()
[4, 6, 8, 10]

1
matos.multiprocessing також має асинхронну карту ( amap), яка дозволяє використовувати смуги прогресу та інші асинхронні програми.
Майк Маккернс

Мені подобається патос. Багатопроцесорний процес, який може послужити майже заміною непаралельної карти, насолоджуючись багатопроцесорною обробкою. У мене є проста обгортка патос.multiprocessing.map, така, що вона є більш ефективною в пам'яті при обробці великої структури даних лише для читання в декількох ядрах, дивіться це сховище git .
Fashandge

Здається цікавим, але він не встановлюється. Це повідомлення, яке надає піп:Could not find a version that satisfies the requirement pp==1.5.7-pathos (from pathos)
xApple

1
Так. Я не випускав через деякий час, оскільки я розбивав функціональність на окремі пакети, а також перетворював на 2/3 сумісний код. Значна частина вищезгаданого була модульована в сумі multiprocess2/3. Див stackoverflow.com/questions/27873093 / ... і pypi.python.org/pypi/multiprocess .
Майк Маккернс

3
@xApple: Подібно до подальших заходів, pathosвін отримав новий стабільний випуск, а також сумісний з 2.x та 3.x.
Майк Маккернс

40

Наразі не існує рішення вашої проблеми, наскільки я знаю: функція, яку ви надаєте, map()повинна бути доступною через імпорт вашого модуля. Ось чому працює код Роберта: функцію f()можна отримати, імпортуючи такий код:

def f(x):
    return x*x

class Calculate(object):
    def run(self):
        p = Pool()
        return p.map(f, [1,2,3])

if __name__ == '__main__':
    cl = Calculate()
    print cl.run()

Я фактично додав "основний" розділ, тому що це відповідає рекомендаціям для платформи Windows ("Переконайтеся, що новий модуль може бути безпечно імпортований новим інтерпретатором Python, не викликаючи небажаних побічних ефектів").

Я також додав великі літери перед Calculate, щоб слідувати PEP 8 . :)


18

Рішення за допомогою mrule правильне, але має помилку: якщо дитина надсилає назад велику кількість даних, вона може заповнити буфер труби, блокуючи дитину pipe.send(), тоді як батько чекає, поки дитина вийде pipe.join(). Рішення полягає в тому, щоб прочитати дані дитини перед тим, як провести join()дитину. Крім того, дитина повинна закрити батьківський кінець труби, щоб запобігти тупику. Нижче наведений код виправляє це. Також пам’ятайте, що це parmapстворює один процес на елемент в X. Більш вдосконалене рішення - використовувати, multiprocessing.cpu_count()щоб розділити Xїх на кілька фрагментів, а потім об'єднати результати перед поверненням. Я залишаю це як вправу для читача, щоб не зіпсувати лаконічність приємної відповіді. ;)

from multiprocessing import Process, Pipe
from itertools import izip

def spawn(f):
    def fun(ppipe, cpipe,x):
        ppipe.close()
        cpipe.send(f(x))
        cpipe.close()
    return fun

def parmap(f,X):
    pipe=[Pipe() for x in X]
    proc=[Process(target=spawn(f),args=(p,c,x)) for x,(p,c) in izip(X,pipe)]
    [p.start() for p in proc]
    ret = [p.recv() for (p,c) in pipe]
    [p.join() for p in proc]
    return ret

if __name__ == '__main__':
    print parmap(lambda x:x**x,range(1,5))

Як ви обираєте кількість процесів?
patapouf_ai

Однак він гине досить швидко через помилку OSError: [Errno 24] Too many open files. Я думаю, що для правильної роботи повинні існувати якісь обмеження щодо кількості процесів ...
patapouf_ai

13

Я також боровся з цим. У мене були функції членів даних класу, як спрощений приклад:

from multiprocessing import Pool
import itertools
pool = Pool()
class Example(object):
    def __init__(self, my_add): 
        self.f = my_add  
    def add_lists(self, list1, list2):
        # Needed to do something like this (the following line won't work)
        return pool.map(self.f,list1,list2)  

Мені потрібно було використовувати функцію self.f у виклику Pool.map () з одного класу, а self.f не сприймав кортеж як аргумент. Оскільки ця функція була вбудована в клас, мені було не ясно, як написати тип обгортки, запропоновані інші відповіді.

Я вирішив цю проблему, використовуючи іншу обгортку, яка приймає кортеж / список, де перший елемент - це функція, а решта елементи - аргументи цієї функції, що називається eval_func_tuple (f_args). Використовуючи це, проблемну лінію можна замінити на return pool.map (eval_func_tuple, itertools.izip (itertools.repeat (self.f), list1, list2)). Ось повний код:

Файл: util.py

def add(a, b): return a+b

def eval_func_tuple(f_args):
    """Takes a tuple of a function and args, evaluates and returns result"""
    return f_args[0](*f_args[1:])  

Файл: main.py

from multiprocessing import Pool
import itertools
import util  

pool = Pool()
class Example(object):
    def __init__(self, my_add): 
        self.f = my_add  
    def add_lists(self, list1, list2):
        # The following line will now work
        return pool.map(util.eval_func_tuple, 
            itertools.izip(itertools.repeat(self.f), list1, list2)) 

if __name__ == '__main__':
    myExample = Example(util.add)
    list1 = [1, 2, 3]
    list2 = [10, 20, 30]
    print myExample.add_lists(list1, list2)  

Запуск main.py дасть [11, 22, 33]. Не соромтеся покращити це, наприклад, eval_func_tuple також можна змінити, щоб прийняти аргументи ключових слів.

З іншого зауваження, в інших відповідях функція "parmap" може бути ефективнішою для більшої кількості процесів, ніж кількість доступних процесорів. Я копіюю відредаговану версію нижче. Це моя перша публікація, і я не був впевнений, чи повинен я безпосередньо відредагувати оригінальну відповідь. Я також перейменував деякі змінні.

from multiprocessing import Process, Pipe  
from itertools import izip  

def spawn(f):  
    def fun(pipe,x):  
        pipe.send(f(x))  
        pipe.close()  
    return fun  

def parmap(f,X):  
    pipe=[Pipe() for x in X]  
    processes=[Process(target=spawn(f),args=(c,x)) for x,(p,c) in izip(X,pipe)]  
    numProcesses = len(processes)  
    processNum = 0  
    outputList = []  
    while processNum < numProcesses:  
        endProcessNum = min(processNum+multiprocessing.cpu_count(), numProcesses)  
        for proc in processes[processNum:endProcessNum]:  
            proc.start()  
        for proc in processes[processNum:endProcessNum]:  
            proc.join()  
        for proc,c in pipe[processNum:endProcessNum]:  
            outputList.append(proc.recv())  
        processNum = endProcessNum  
    return outputList    

if __name__ == '__main__':  
    print parmap(lambda x:x**x,range(1,5))         

8

Я взяв відповідь klaus se та aganders3 і зробив документально підтверджений модуль, який читається і зберігається в одному файлі. Ви можете просто додати його до свого проекту. Він навіть має додаткову панель прогресу!

"""
The ``processes`` module provides some convenience functions
for using parallel processes in python.

Adapted from http://stackoverflow.com/a/16071616/287297

Example usage:

    print prll_map(lambda i: i * 2, [1, 2, 3, 4, 6, 7, 8], 32, verbose=True)

Comments:

"It spawns a predefined amount of workers and only iterates through the input list
 if there exists an idle worker. I also enabled the "daemon" mode for the workers so
 that KeyboardInterupt works as expected."

Pitfalls: all the stdouts are sent back to the parent stdout, intertwined.

Alternatively, use this fork of multiprocessing: 
https://github.com/uqfoundation/multiprocess
"""

# Modules #
import multiprocessing
from tqdm import tqdm

################################################################################
def apply_function(func_to_apply, queue_in, queue_out):
    while not queue_in.empty():
        num, obj = queue_in.get()
        queue_out.put((num, func_to_apply(obj)))

################################################################################
def prll_map(func_to_apply, items, cpus=None, verbose=False):
    # Number of processes to use #
    if cpus is None: cpus = min(multiprocessing.cpu_count(), 32)
    # Create queues #
    q_in  = multiprocessing.Queue()
    q_out = multiprocessing.Queue()
    # Process list #
    new_proc  = lambda t,a: multiprocessing.Process(target=t, args=a)
    processes = [new_proc(apply_function, (func_to_apply, q_in, q_out)) for x in range(cpus)]
    # Put all the items (objects) in the queue #
    sent = [q_in.put((i, x)) for i, x in enumerate(items)]
    # Start them all #
    for proc in processes:
        proc.daemon = True
        proc.start()
    # Display progress bar or not #
    if verbose:
        results = [q_out.get() for x in tqdm(range(len(sent)))]
    else:
        results = [q_out.get() for x in range(len(sent))]
    # Wait for them to finish #
    for proc in processes: proc.join()
    # Return results #
    return [x for i, x in sorted(results)]

################################################################################
def test():
    def slow_square(x):
        import time
        time.sleep(2)
        return x**2
    objs    = range(20)
    squares = prll_map(slow_square, objs, 4, verbose=True)
    print "Result: %s" % squares

EDIT : Додано пропозицію @ alexander-mcfarlane та тестову функцію


одне питання з панеллю прогресу ... Панель вимірює лише те, наскільки неефективно розподілено навантаження на процесори. Якщо навантаження ідеально розділена, то всі процесори будуть join()одночасно, і ви просто отримаєте спалах 100%завершеного на tqdmдисплеї. Єдиний раз, коли це стане в нагоді, якщо кожен процесор має необ’єктивну навантаження
Олександр Макфарлейн

1
переходимо tqdm()до завершення лінії: result = [q_out.get() for _ in tqdm(sent)]і це працює набагато краще - великі зусилля, хоча дуже цінуємо це, тому +1
Олександр Макфарлейн

Дякую за ту пораду, я спробую її, а потім оновлю відповідь!
xApple

Відповідь оновлюється, і панель прогресу працює набагато краще!
xApple

8

Я знаю, що це було запропоновано понад 6 років тому, але я просто хотів додати своє рішення, оскільки деякі пропозиції вище виглядають жахливо складними, але моє рішення насправді було дуже простим.

Все, що мені потрібно було зробити - це запустити виклик pool.map () до функції помічника. Передача об’єкта класу разом із аргументами для методу як кортеж, який виглядав приблизно так.

def run_in_parallel(args):
    return args[0].method(args[1])

myclass = MyClass()
method_args = [1,2,3,4,5,6]
args_map = [ (myclass, arg) for arg in method_args ]
pool = Pool()
pool.map(run_in_parallel, args_map)

7

Функції, визначені в класах (навіть у межах функцій в межах класів), насправді не підбираються. Однак це працює:

def f(x):
    return x*x

class calculate(object):
    def run(self):
        p = Pool()
    return p.map(f, [1,2,3])

cl = calculate()
print cl.run()

15
дякую, але я вважаю трохи брудним визначити функцію поза класом. Клас повинен поєднати все необхідне для досягнення заданого завдання.
Мермоз

3
@Memoz: "Клас повинен поєднати все, що йому потрібно" Дійсно? Я не можу знайти багато прикладів цього. Більшість класів залежать від інших класів або функцій. Чому залежність класу називати "брудною"? Що не так із залежністю?
S.Lott

Ну, а функція не повинна змінювати існуючі дані класу - тому що вона модифікувала б версію в іншому процесі - так це може бути статичний метод. Ви можете сортувати соління статичним методом: stackoverflow.com/questions/1914261/… Або для чогось цього дрібниці ви можете використати лямбда.
Роберт

6

Я знаю, що це питання було задано 8 років і 10 місяців тому, але я хочу представити вам своє рішення:

from multiprocessing import Pool

class Test:

    def __init__(self):
        self.main()

    @staticmethod
    def methodForMultiprocessing(x):
        print(x*x)

    def main(self):
        if __name__ == "__main__":
            p = Pool()
            p.map(Test.methodForMultiprocessing, list(range(1, 11)))
            p.close()

TestObject = Test()

Вам просто потрібно перетворити функцію класу в статичний метод. Але це також можливо методом класу:

from multiprocessing import Pool

class Test:

    def __init__(self):
        self.main()

    @classmethod
    def methodForMultiprocessing(cls, x):
        print(x*x)

    def main(self):
        if __name__ == "__main__":
            p = Pool()
            p.map(Test.methodForMultiprocessing, list(range(1, 11)))
            p.close()

TestObject = Test()

Випробувано на Python 3.7.3


3

Я змінив метод klaus se, тому що, коли він працював на мене з невеликими списками, він буде звисати, коли кількість елементів становила ~ 1000 або більше. Замість того, щоб штовхати завдання по черзі з Noneумовою зупинки, я завантажую вхідну чергу всі відразу і просто дозволю процесам набиратися на неї, поки вона не порожня.

from multiprocessing import cpu_count, Queue, Process

def apply_func(f, q_in, q_out):
    while not q_in.empty():
        i, x = q_in.get()
        q_out.put((i, f(x)))

# map a function using a pool of processes
def parmap(f, X, nprocs = cpu_count()):
    q_in, q_out   = Queue(), Queue()
    proc = [Process(target=apply_func, args=(f, q_in, q_out)) for _ in range(nprocs)]
    sent = [q_in.put((i, x)) for i, x in enumerate(X)]
    [p.start() for p in proc]
    res = [q_out.get() for _ in sent]
    [p.join() for p in proc]

    return [x for i,x in sorted(res)]

Редагувати: на жаль, зараз я стикаюся з цією помилкою в моїй системі: Максимальний розмір черги багатопроцесорної черги - 32767 , сподіваємось, вирішення проблем допоможе.


1

Ви можете запустити свій код без будь-яких проблем, якщо ви якось вручну проігноруєте Poolоб'єкт зі списку об’єктів у класі, оскільки він не в pickleзмозі, як говорить помилка. Це можна зробити за допомогою __getstate__функції (дивіться також тут ), як слідує. PoolОб'єкт буде намагатися знайти __getstate__і __setstate__функцію і виконувати їх , якщо він знаходить його при запуску map, і map_asyncт.д .:

class calculate(object):
    def __init__(self):
        self.p = Pool()
    def __getstate__(self):
        self_dict = self.__dict__.copy()
        del self_dict['p']
        return self_dict
    def __setstate__(self, state):
        self.__dict__.update(state)

    def f(self, x):
        return x*x
    def run(self):
        return self.p.map(self.f, [1,2,3])

Потім зробіть:

cl = calculate()
cl.run()

дасть вам вихід:

[1, 4, 9]

Я тестував вищевказаний код у Python 3.x, і він працює.


0

Я не впевнений, чи був застосований такий підхід, але робота, яку я використовую:

from multiprocessing import Pool

t = None

def run(n):
    return t.f(n)

class Test(object):
    def __init__(self, number):
        self.number = number

    def f(self, x):
        print x * self.number

    def pool(self):
        pool = Pool(2)
        pool.map(run, range(10))

if __name__ == '__main__':
    t = Test(9)
    t.pool()
    pool = Pool(2)
    pool.map(run, range(10))

Вихід повинен бути:

0
9
18
27
36
45
54
63
72
81
0
9
18
27
36
45
54
63
72
81

0
class Calculate(object):
  # Your instance method to be executed
  def f(self, x, y):
    return x*y

if __name__ == '__main__':
  inp_list = [1,2,3]
  y = 2
  cal_obj = Calculate()
  pool = Pool(2)
  results = pool.map(lambda x: cal_obj.f(x, y), inp_list)

Існує можливість, що ви хочете застосувати цю функцію для кожного різного екземпляра класу. Тоді ось і рішення для цього

class Calculate(object):
  # Your instance method to be executed
  def __init__(self, x):
    self.x = x

  def f(self, y):
    return self.x*y

if __name__ == '__main__':
  inp_list = [Calculate(i) for i in range(3)]
  y = 2
  pool = Pool(2)
  results = pool.map(lambda x: x.f(y), inp_list)

0

Ось моє рішення, яке, на мою думку, трохи менше хакітське, ніж більшість інших тут. Це схоже на відповідь nightowl.

someclasses = [MyClass(), MyClass(), MyClass()]

def method_caller(some_object, some_method='the method'):
    return getattr(some_object, some_method)()

othermethod = partial(method_caller, some_method='othermethod')

with Pool(6) as pool:
    result = pool.map(othermethod, someclasses)

0

З http://www.rueckstiess.net/research/snippets/show/ca1d7d90 та http://qingkaikong.blogspot.com/2016/12/python-parallel-method-in-class.html

Ми можемо зробити зовнішню функцію і засіяти її об’єктом класу self:

from joblib import Parallel, delayed
def unwrap_self(arg, **kwarg):
    return square_class.square_int(*arg, **kwarg)

class square_class:
    def square_int(self, i):
        return i * i

    def run(self, num):
        results = []
        results = Parallel(n_jobs= -1, backend="threading")\
            (delayed(unwrap_self)(i) for i in zip([self]*len(num), num))
        print(results)

АБО без роботи:

from multiprocessing import Pool
import time

def unwrap_self_f(arg, **kwarg):
    return C.f(*arg, **kwarg)

class C:
    def f(self, name):
        print 'hello %s,'%name
        time.sleep(5)
        print 'nice to meet you.'

    def run(self):
        pool = Pool(processes=2)
        names = ('frank', 'justin', 'osi', 'thomas')
        pool.map(unwrap_self_f, zip([self]*len(names), names))

if __name__ == '__main__':
    c = C()
    c.run()

0

Це може бути не дуже вдалим рішенням, але в моєму випадку я вирішую це так.

from multiprocessing import Pool

def foo1(data):
    self = data.get('slf')
    lst = data.get('lst')
    return sum(lst) + self.foo2()

class Foo(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def foo2(self):
        return self.a**self.b   

    def foo(self):
        p = Pool(5)
        lst = [1, 2, 3]
        result = p.map(foo1, (dict(slf=self, lst=lst),))
        return result

if __name__ == '__main__':
    print(Foo(2, 4).foo())

Мені довелося перейти selfдо своєї функції, оскільки я маю доступ до атрибутів та функцій свого класу через цю функцію. Це працює для мене. Виправлення та пропозиції завжди вітаються.


0

Ось котла, яку я написав для використання багатопроцесорного пулу в python3, зокрема python3.7.7 використовувався для запуску тестів. Я отримав свої найшвидші пробіжки з використанням imap_unordered. Просто підключіть свій сценарій і спробуйте його. Ви можете скористатися timeitабо просто time.time()зрозуміти, що найкраще підходить для вас.

import multiprocessing
import time

NUMBER_OF_PROCESSES = multiprocessing.cpu_count()
MP_FUNCTION = 'starmap'  # 'imap_unordered' or 'starmap' or 'apply_async'

def process_chunk(a_chunk):
    print(f"processig mp chunk {a_chunk}")
    return a_chunk


map_jobs = [1, 2, 3, 4]

result_sum = 0

s = time.time()
if MP_FUNCTION == 'imap_unordered':
    pool = multiprocessing.Pool(processes=NUMBER_OF_PROCESSES)
    for i in pool.imap_unordered(process_chunk, map_jobs):
        result_sum += i
elif MP_FUNCTION == 'starmap':
    pool = multiprocessing.Pool(processes=NUMBER_OF_PROCESSES)
    try:
        map_jobs = [(i, ) for i in map_jobs]
        result_sum = pool.starmap(process_chunk, map_jobs)
        result_sum = sum(result_sum)
    finally:
        pool.close()
        pool.join()
elif MP_FUNCTION == 'apply_async':
    with multiprocessing.Pool(processes=NUMBER_OF_PROCESSES) as pool:
        result_sum = [pool.apply_async(process_chunk, [i, ]).get() for i in map_jobs]
    result_sum = sum(result_sum)
print(f"result_sum is {result_sum}, took {time.time() - s}s")

Наведений вище сценарій imap_unorderedнасправді здається найгіршим для мене. Спробуйте свій корпус і порівняйте його на машині, на якій ви плануєте його запустити. Читайте також у пулах процесів . Ура!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.