Đa xử lý: Làm thế nào để sử dụng Pool.map trên một hàm được định nghĩa trong một lớp?


179

Khi tôi chạy một cái gì đó như:

from multiprocessing import Pool

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

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

nó hoạt động tốt. Tuy nhiên, đặt điều này như là một chức năng của một lớp:

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()

Cung cấp cho tôi các lỗi sau:

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

Tôi đã thấy một bài đăng từ Alex Martelli xử lý cùng loại vấn đề, nhưng nó không đủ rõ ràng.


1
"Đây là một chức năng của một lớp"? Bạn có thể gửi mã thực sự nhận được lỗi thực tế. Không có mã thực tế, chúng tôi chỉ có thể đoán những gì bạn đang làm sai.
S.Lott

Như một nhận xét chung, tồn tại các mô đun tẩy mạnh hơn mô đun ngâm tiêu chuẩn của Python (như mô đun picloud được đề cập trong câu trả lời này ).
klaus se

1
Tôi đã có một vấn đề tương tự với việc đóng cửa IPython.Parallel, nhưng ở đó bạn có thể khắc phục vấn đề bằng cách đẩy các đối tượng đến các nút. Có vẻ khá khó chịu khi giải quyết vấn đề này với đa xử lý.
Alex S

Dưới đây calculatelà picklable, vì vậy nó có vẻ như điều này có thể được giải quyết bằng 1) tạo ra một đối tượng hàm với một constructor rằng bản sao trên một calculateví dụ và sau đó 2) đi qua một thể hiện của đối tượng chức năng này để Pool's mapphương pháp. Không?
Thứ

1
@math Tôi không tin bất kỳ "thay đổi gần đây" nào của Python sẽ giúp ích được gì. Một số hạn chế của multiprocessingmô-đun là do mục tiêu của nó là triển khai đa nền tảng và thiếu fork(2)cuộc gọi hệ thống giống như trong Windows. Nếu bạn không quan tâm đến hỗ trợ Win32, có thể có một cách giải quyết dựa trên quy trình đơn giản hơn. Hoặc nếu bạn chuẩn bị sử dụng các luồng thay vì các quy trình, bạn có thể thay thế from multiprocessing import Poolbằng from multiprocessing.pool import ThreadPool as Pool.
Aya

Câu trả lời:


69

Tôi cũng cảm thấy khó chịu vì những hạn chế về loại chức năng pool.map có thể chấp nhận. Tôi đã viết như sau để phá vỡ điều này. Nó xuất hiện để làm việc, ngay cả đối với việc sử dụng đệ quy 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
Điều này đã làm việc rất tốt cho tôi, cảm ơn bạn. Tôi đã tìm thấy một điểm yếu: Tôi đã thử sử dụng parmap trên một số chức năng được truyền xung quanh một defaultdict và nhận lại PicklingError. Tôi đã không tìm ra một giải pháp cho vấn đề này, tôi chỉ làm lại mã của mình để không sử dụng defaultdict.
sans

2
Điều này không hoạt động trong Python 2.7.2 (mặc định, ngày 12 tháng 6 năm 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] trên win32
ubershmekel

3
Điều này không hoạt động trên Python 2.7.3 ngày 1 tháng 8 năm 2012, 05:14:39. Điều này không hoạt động trên các iterables khổng lồ -> nó gây ra OSError: [Errno 24] Quá nhiều tệp đang mở do số lượng đường ống mà nó mở.
Eiyrioü von Kauyf

Giải pháp này sinh ra một quy trình cho từng hạng mục công việc. Giải pháp "klaus se" dưới đây hiệu quả hơn.
ypnos

85

Tôi không thể sử dụng các mã được đăng cho đến nay vì các mã sử dụng "multrocessing.Pool" không hoạt động với các biểu thức lambda và các mã không sử dụng "Multiprocessing.Pool" sinh ra nhiều quy trình như có các mục công việc.

Tôi đã điều chỉnh mã st nó sinh ra một lượng công nhân được xác định trước và chỉ lặp qua danh sách đầu vào nếu có một công nhân nhàn rỗi. Tôi cũng đã bật chế độ "daemon" cho công nhân st ctrl-c hoạt động như mong đợi.

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
Làm thế nào bạn có được một thanh tiến trình để làm việc đúng với parmapchức năng này ?
shockburner

2
Một câu hỏi - Tôi đã sử dụng giải pháp này nhưng nhận thấy rằng các quá trình python tôi sinh ra vẫn hoạt động trong bộ nhớ. Bất kỳ suy nghĩ nhanh chóng về làm thế nào để giết những người khi parmap của bạn thoát?
CompEcon

1
@ klaus-se Tôi biết chúng tôi không khuyến khích chỉ nói cảm ơn trong các bình luận, nhưng câu trả lời của bạn quá giá trị đối với tôi, tôi không thể cưỡng lại. Tôi ước tôi có thể mang đến cho bạn nhiều hơn chỉ một danh tiếng ...
deshtop

2
@greole đi qua (None, None)như là mục cuối cùng cho biết funrằng nó đã đạt đến cuối chuỗi các mục cho mỗi quy trình.
aganders3

4
@deshtop: bạn có thể với tiền thưởng, nếu bạn có đủ danh tiếng cho mình :-)
Đánh dấu

57

Đa xử lý và tẩy rửa bị hỏng và giới hạn trừ khi bạn nhảy ra ngoài thư viện tiêu chuẩn.

Nếu bạn sử dụng một nhánh multiprocessinggọi pathos.multiprocesssing, bạn có thể trực tiếp sử dụng các lớp và phương thức lớp trong các maphàm đa xử lý . Điều này là do dillđược sử dụng thay vì picklehoặc cPickle, và dillcó thể tuần tự hóa hầu hết mọi thứ trong python.

pathos.multiprocessingcũng cung cấp một hàm bản đồ không đồng bộ, và nó có thể maphoạt động với nhiều đối số (ví dụ map(math.pow, [1,2,3], [4,5,6]))

Xem các cuộc thảo luận: Điều gì có thể đa xử lý và thì là làm gì với nhau?

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

Nó thậm chí còn xử lý mã bạn đã viết ban đầu, mà không sửa đổi và từ trình thông dịch. Tại sao bất cứ điều gì khác mong manh và cụ thể hơn cho một trường hợp duy nhất?

>>> 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]

Nhận mã tại đây: https://github.com/uqfoundation/pathos

Và, chỉ để thể hiện thêm một chút về những gì nó có thể làm:

>>> 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
pathos.multiprocessing cũng có bản đồ không đồng bộ ( amap) cho phép sử dụng các thanh tiến trình và lập trình không đồng bộ khác.
Mike McKerns

Tôi thích pathos.multiprocessing, có thể phục vụ gần như thay thế bản đồ không song song trong khi thưởng thức đa xử lý. Tôi có một trình bao bọc đơn giản của pathos.multiprocessing.map, sao cho hiệu quả bộ nhớ hơn khi xử lý cấu trúc dữ liệu lớn chỉ đọc trên nhiều lõi, xem kho lưu trữ git này .
Fashandge

Có vẻ thú vị, nhưng nó không cài đặt. Đây là thông điệp mà pip đưa ra:Could not find a version that satisfies the requirement pp==1.5.7-pathos (from pathos)
xApple

1
Đúng. Tôi đã không phát hành trong một thời gian vì tôi đã chia chức năng thành các gói riêng biệt và cũng chuyển đổi thành 2/3 mã tương thích. Phần lớn ở trên đã được mô đun hóa trong multiprocessđó tương thích 2/3. Xem stackoverflow.com/questions/27873093/ vòipypi.python.org/pypi/multiprocess .
Mike McKerns

3
@xApple: Cũng như một phần tiếp theo, pathosđã có một bản phát hành ổn định mới và cũng tương thích 2.x và 3.x.
Mike McKerns

40

Hiện tại không có giải pháp nào cho vấn đề của bạn, theo như tôi biết: chức năng mà bạn cung cấp map()phải có thể truy cập được thông qua việc nhập mô-đun của bạn. Đây là lý do tại sao mã của robert hoạt động: f()có thể lấy được hàm bằng cách nhập mã sau:

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()

Tôi thực sự đã thêm một phần "chính", bởi vì phần này tuân theo các khuyến nghị cho nền tảng Windows ("Đảm bảo rằng mô-đun chính có thể được nhập bởi trình thông dịch Python mới mà không gây ra tác dụng phụ ngoài ý muốn").

Tôi cũng đã thêm một chữ cái viết hoa ở phía trước Calculate, để làm theo PEP 8 . :)


18

Giải pháp của mrule là chính xác nhưng có một lỗi: nếu đứa trẻ gửi lại một lượng lớn dữ liệu, nó có thể lấp đầy bộ đệm của đường ống, chặn đứa trẻ pipe.send(), trong khi cha mẹ đang chờ đứa trẻ thoát ra pipe.join(). Giải pháp là đọc dữ liệu của trẻ trước khi join()cho trẻ ăn. Hơn nữa, trẻ nên đóng đầu ống của cha mẹ để tránh bế tắc. Mã dưới đây sửa lỗi đó. Cũng lưu ý rằng điều này parmaptạo ra một quá trình cho mỗi phần tử trong X. Một giải pháp tiên tiến hơn là sử dụng multiprocessing.cpu_count()để chia Xthành một số khối, và sau đó hợp nhất các kết quả trước khi quay trở lại. Tôi để nó như một bài tập cho người đọc để không làm hỏng sự đơn giản của câu trả lời hay bởi mrule. ;)

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))

Làm thế nào để bạn chọn số lượng các quy trình?
patapouf_ai

Tuy nhiên nó chết khá nhanh vì lỗi OSError: [Errno 24] Too many open files. Tôi nghĩ rằng cần phải có một số loại giới hạn về số lượng quy trình để nó hoạt động chính xác ...
patapouf_ai

13

Tôi cũng đã vật lộn với điều này. Tôi có các chức năng là thành viên dữ liệu của một lớp, như một ví dụ đơn giản:

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)  

Tôi cần sử dụng hàm self.f trong lệnh gọi Pool.map () từ trong cùng một lớp và self.f không lấy một tuple làm đối số. Vì hàm này được nhúng trong một lớp, nên tôi không rõ cách viết kiểu trình bao bọc mà các câu trả lời khác được đề xuất.

Tôi đã giải quyết vấn đề này bằng cách sử dụng một trình bao bọc khác có một tuple / list, trong đó phần tử đầu tiên là hàm và các phần tử còn lại là đối số cho hàm đó, được gọi là eval_func_tuple (f_args). Sử dụng điều này, dòng có vấn đề có thể được thay thế bằng return pool.map (eval_func_tuple, itertools.izip (itertools.repeat (self.f), list1, list2)). Đây là mã đầy đủ:

Tập tin:

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:])  

Tập tin: 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)  

Chạy main.txt sẽ cho [11, 22, 33]. Vui lòng cải thiện điều này, ví dụ eval_func_tuple cũng có thể được sửa đổi để lấy các đối số từ khóa.

Một lưu ý khác, trong một câu trả lời khác, chức năng "parmap" có thể được thực hiện hiệu quả hơn trong trường hợp có nhiều Quá trình hơn số lượng CPU có sẵn. Tôi đang sao chép một phiên bản chỉnh sửa dưới đây. Đây là bài viết đầu tiên của tôi và tôi không chắc mình có nên trực tiếp chỉnh sửa câu trả lời ban đầu hay không. Tôi cũng đổi tên một số biến.

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

Tôi đã lấy câu trả lời của klaus se và aganders3, và tạo ra một mô-đun tài liệu dễ đọc hơn và giữ trong một tệp. Bạn chỉ có thể thêm nó vào dự án của bạn. Nó thậm chí có một thanh tiến trình tùy chọn!

"""
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 : Đã thêm đề xuất @ alexander-mcfarlane và chức năng kiểm tra


một vấn đề với thanh tiến trình của bạn ... Thanh chỉ đo mức độ không hiệu quả của khối lượng công việc được phân chia trên các bộ xử lý. Nếu khối lượng công việc được phân chia hoàn hảo thì tất cả các bộ xử lý sẽ join()đồng thời và bạn sẽ chỉ nhận được một đèn flash 100%hoàn thành trong tqdmmàn hình. Lần duy nhất nó sẽ hữu ích là nếu mỗi bộ xử lý có khối lượng công việc sai lệch
Alexander McFarlane

1
di chuyển tqdm()để kết thúc đường dây: result = [q_out.get() for _ in tqdm(sent)]và nó hoạt động tốt hơn rất nhiều - nỗ lực rất lớn mặc dù thực sự đánh giá cao điều này nên +1
Alexander McFarlane

Cảm ơn lời khuyên đó, tôi sẽ thử nó và sau đó cập nhật câu trả lời!
xApple

Câu trả lời được cập nhật, và thanh tiến trình hoạt động tốt hơn nhiều!
xApple

8

Tôi biết điều này đã được hỏi hơn 6 năm trước, nhưng chỉ muốn thêm giải pháp của tôi, vì một số gợi ý ở trên có vẻ phức tạp khủng khiếp, nhưng giải pháp của tôi thực sự rất đơn giản.

Tất cả những gì tôi phải làm là kết thúc lệnh gọi pool.map () tới hàm trợ giúp. Truyền đối tượng lớp cùng với đối số cho phương thức là một tuple, trông giống như thế này.

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

Các hàm được định nghĩa trong các lớp (ngay cả trong các hàm trong các lớp) không thực sự cầu kỳ. Tuy nhiên, điều này hoạt động:

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
cảm ơn, nhưng tôi thấy nó hơi bẩn khi định nghĩa hàm ngoài lớp. Lớp nên bó tất cả những gì nó cần để đạt được một nhiệm vụ nhất định.
Mermoz

3
@Memoz: "Lớp nên gói tất cả những gì nó cần" Thật sao? Tôi không thể tìm thấy nhiều ví dụ về điều này. Hầu hết các lớp phụ thuộc vào các lớp hoặc chức năng khác. Tại sao gọi một phụ thuộc lớp là "bẩn"? Có gì sai với một phụ thuộc?
S.Lott

Chà, hàm không nên sửa đổi dữ liệu lớp hiện có - bởi vì nó sẽ sửa đổi phiên bản trong quy trình khác - vì vậy nó có thể là một phương thức tĩnh. Bạn có thể sắp xếp một phương thức tĩnh: stackoverflow.com/questions/1914261/ Khăn Hoặc, đối với một cái gì đó tầm thường này, bạn có thể sử dụng lambda.
robert

6

Tôi biết rằng câu hỏi này đã được hỏi 8 năm và 10 tháng trước nhưng tôi muốn trình bày cho bạn giải pháp của tôi:

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()

Bạn chỉ cần làm cho hàm lớp của bạn thành một phương thức tĩnh. Nhưng nó cũng có thể với một phương thức lớp:

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()

Đã thử nghiệm trong Python 3.7.3


3

Tôi đã sửa đổi phương pháp của klaus se vì trong khi nó hoạt động với tôi với các danh sách nhỏ, nó sẽ bị treo khi số lượng vật phẩm là ~ 1000 hoặc lớn hơn. Thay vì đẩy từng công việc một lúc với Noneđiều kiện dừng, tôi tải lên hàng đợi đầu vào cùng một lúc và chỉ để các quy trình nhai nó cho đến khi nó trống.

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)]

Chỉnh sửa: thật không may bây giờ tôi đang gặp phải lỗi này trên hệ thống của mình: Giới hạn tối đa hóa hàng đợi đa xử lý là 32767 , hy vọng các cách giải quyết ở đó sẽ giúp ích.


1

Bạn có thể chạy mã của mình mà không gặp vấn đề gì nếu bạn bằng cách nào đó bỏ qua Poolđối tượng khỏi danh sách các đối tượng trong lớp vì nó không picklethể như lỗi nói. Bạn có thể làm điều này với __getstate__chức năng (xem ở đây nữa) như sau. Đối Pooltượng sẽ cố gắng tìm __getstate__và các __setstate__hàm và thực thi chúng nếu nó tìm thấy nó khi bạn chạy map, map_asyncv.v.

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])

Sau đó làm:

cl = calculate()
cl.run()

sẽ cung cấp cho bạn đầu ra:

[1, 4, 9]

Tôi đã kiểm tra mã trên trong Python 3.x và nó hoạt động.


0

Tôi không chắc liệu phương pháp này đã được thực hiện chưa nhưng một công việc xung quanh tôi đang sử dụng là:

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))

Đầu ra phải là:

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)

Có khả năng bạn muốn áp dụng hàm này cho từng thể hiện khác nhau của lớp. Sau đó, đây là giải pháp cho điều đó

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

Đây là giải pháp của tôi, mà tôi nghĩ là ít hack hơn so với hầu hết những người khác ở đây. Nó tương tự như câu trả lời của 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

Từ http://www.rueckstiess.net/research/snippets/show/ca1d7d90http:// Khánhkaikong.blogspot.com/2016/12/python-abul-method-in- class.html

Chúng ta có thể tạo một hàm bên ngoài và khởi tạo nó với đối tượng tự lớp:

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)

HOẶC không có joblib:

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

Đây có thể không phải là một giải pháp rất tốt nhưng trong trường hợp của tôi, tôi giải quyết nó như thế này.

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())

Tôi đã phải chuyển selfđến chức năng của mình vì tôi phải truy cập các thuộc tính và chức năng của lớp thông qua chức năng đó. Đây là làm việc cho tôi. Sửa chữa và đề xuất luôn được chào đón.


0

Dưới đây là bản tóm tắt tôi đã viết để sử dụng Pool đa xử lý trong python3, cụ thể là python3.7.7 đã được sử dụng để chạy thử nghiệm. Tôi đã chạy nhanh nhất của tôi bằng cách sử dụng imap_unordered. Chỉ cần cắm vào kịch bản của bạn và thử nó. Bạn có thể sử dụng timeithoặc chỉ time.time()để tìm ra cái nào phù hợp nhất với bạn.

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")

Trong kịch bản imap_unorderedtrên dường như thực sự tồi tệ nhất đối với tôi. Hãy thử trường hợp của bạn và điểm chuẩn nó trên máy bạn định chạy nó. Cũng đọc lên trên Process Pools . Chúc mừng!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.