2010-09-08 33 views
26

Tôi có một đối tượng Python khá phức tạp mà tôi cần chia sẻ giữa nhiều quy trình. Tôi khởi chạy các quy trình này bằng cách sử dụng multiprocessing.Process. Khi tôi chia sẻ một đối tượng với multiprocessing.Queuemultiprocessing.Pipe trong đó, chúng được chia sẻ tốt. Nhưng khi tôi cố gắng chia sẻ một đối tượng với các đối tượng mô-đun không đa xử lý khác, có vẻ như Python đã dồn các đối tượng này. Điều đó có đúng không?Chia sẻ một đối tượng phức tạp giữa các quy trình Python?

Tôi đã thử sử dụng multiprocessing.Value. Nhưng tôi không chắc loại đó nên là gì? Lớp đối tượng của tôi được gọi là MyClass. Nhưng khi tôi cố gắng multiprocess.Value(MyClass, instance), nó không thành công với:

TypeError: this type has no size

ý tưởng Bất kỳ những gì đang xảy ra?

+1

liên quan: http://stackoverflow.com/questions/659865/python-multiprocessing-sharing-a-large-read-only-object-between-processes – tokland

Trả lời

23

Bạn có thể thực hiện việc này bằng cách sử dụng các lớp "Quản lý" đa xử lý của Python và lớp proxy mà bạn xác định. Từ tài liệu Python: http://docs.python.org/library/multiprocessing.html#proxy-objects

Điều bạn muốn làm là định nghĩa lớp proxy cho đối tượng tùy chỉnh của mình, sau đó chia sẻ đối tượng bằng "Trình quản lý từ xa" - xem các ví dụ trong cùng một trang tài liệu được liên kết cho "quản lý từ xa" nơi tài liệu hiển thị cách chia sẻ hàng đợi từ xa. Bạn sẽ làm điều tương tự, nhưng cuộc gọi của bạn tới your_manager_instance.register() sẽ bao gồm lớp proxy tùy chỉnh của bạn trong danh sách đối số của nó.

Bằng cách này, bạn đang thiết lập máy chủ để chia sẻ đối tượng tùy chỉnh với proxy tùy chỉnh. Khách hàng của bạn cần truy cập vào máy chủ (một lần nữa, xem các ví dụ tài liệu tuyệt vời về cách thiết lập quyền truy cập máy khách/máy chủ vào hàng đợi từ xa, nhưng thay vì chia sẻ hàng đợi, bạn đang chia sẻ quyền truy cập vào lớp cụ thể của mình).

+3

Mã trong câu hỏi này đã giúp bổ sung trang tài liệu cho tôi. Đây là một ví dụ với một lớp tùy chỉnh. http://stackoverflow.com/questions/11951750/sharing-object-class-instance-in-python-using-managers – EarlCrapstone

15

Sau nhiều nghiên cứu và thử nghiệm, tôi thấy "Người quản lý" thực hiện công việc này ở mức đối tượng không phức tạp.

Mã dưới đây cho thấy đối tượng inst được chia sẻ giữa các quá trình, có nghĩa là thuộc tính var của inst được thay đổi bên ngoài khi quá trình con thay đổi.

from multiprocessing import Process, Manager 
from multiprocessing.managers import BaseManager 

class SimpleClass(object): 
    def __init__(self): 
     self.var = 0 

    def set(self, value): 
     self.var = value 

    def get(self): 
     return self.var 


def change_obj_value(obj): 
    obj.set(100) 


if __name__ == '__main__': 
    BaseManager.register('SimpleClass', SimpleClass) 
    manager = BaseManager() 
    manager.start() 
    inst = manager.SimpleClass() 

    p = Process(target=change_obj_value, args=[inst]) 
    p.start() 
    p.join() 

    print inst     # <__main__.SimpleClass object at 0x10cf82350> 
    print inst.get()    # 100 

Okay, mã trên là đủ nếu bạn chỉ cần chia sẻ đối tượng đơn giản.

Tại sao không phức tạp? Bởi vì nó có thể thất bại nếu đối tượng của bạn được lồng (object bên trong đối tượng):

from multiprocessing import Process, Manager 
from multiprocessing.managers import BaseManager 

class GetSetter(object): 
    def __init__(self): 
     self.var = None 

    def set(self, value): 
     self.var = value 

    def get(self): 
     return self.var 


class ChildClass(GetSetter): 
    pass 

class ParentClass(GetSetter): 
    def __init__(self): 
     self.child = ChildClass() 
     GetSetter.__init__(self) 

    def getChild(self): 
     return self.child 


def change_obj_value(obj): 
    obj.set(100) 
    obj.getChild().set(100) 


if __name__ == '__main__': 
    BaseManager.register('ParentClass', ParentClass) 
    manager = BaseManager() 
    manager.start() 
    inst2 = manager.ParentClass() 

    p2 = Process(target=change_obj_value, args=[inst2]) 
    p2.start() 
    p2.join() 

    print inst2     # <__main__.ParentClass object at 0x10cf82350> 
    print inst2.getChild()   # <__main__.ChildClass object at 0x10cf6dc50> 
    print inst2.get()    # 100 
    #good! 

    print inst2.getChild().get() # None 
    #bad! you need to register child class too but there's almost no way to do it 
    #even if you did register child class, you may get PicklingError :) 

Tôi nghĩ lý do chính về hành vi này là bởi vì Manager chỉ là một thanh kẹo xây dựng trên đầu trang của các công cụ giao tiếp ở mức độ thấp như ống /xếp hàng.

Vì vậy, cách tiếp cận này là không phải là cũng được đề xuất cho trường hợp đa xử lý. Sẽ tốt hơn nếu bạn có thể sử dụng các công cụ cấp thấp như khóa/semaphore/pipe/queue hoặc các công cụ cấp cao như Hàng đợi Redis hoặc Redis publish/subscribe cho trường hợp sử dụng phức tạp (chỉ giới thiệu của tôi).

+0

Làm thế nào để chia sẻ một đối tượng phức tạp? –

0

Để lưu một số cơn đau đầu với tài nguyên được chia sẻ, bạn có thể cố gắng thu thập dữ liệu cần truy cập vào tài nguyên đơn lẻ trong câu lệnh trả về của hàm được ánh xạ bằng ví dụ:pool.imap_unordered và sau đó tiếp tục xử lý nó trong một vòng lặp để lấy kết quả từng phần:

for result in in pool.imap_unordered(process_function, iterable_data): 
    do_something(result) 

Nếu nó không phải là nhiều dữ liệu đó được trở lại, sau đó có thể không được nhiều chi phí trong việc này.

2

đây là gói python mà tôi đã thực hiện cho điều đó (chia sẻ các đối tượng phức tạp giữa các quy trình).

git: https://github.com/dRoje/pipe-proxy

Ý tưởng là bạn tạo một proxy cho đối tượng của bạn và vượt qua nó để một quá trình. Sau đó, bạn sử dụng proxy như bạn có một tham chiếu đến đối tượng ban đầu. Mặc dù bạn chỉ có thể sử dụng các cuộc gọi phương thức, do đó, việc truy cập các biến đối tượng được thực hiện đã ném các bộ định tuyến và các getters.

Giả sử chúng ta có một đối tượng gọi là ‘dụ’, tạo proxy và nghe proxy là dễ dàng:

from pipeproxy import proxy 
example = Example() 
exampleProxy, exampleProxyListener = proxy.createProxy(example) 

Bây giờ bạn gửi proxy để quá trình khác.

p = Process(target=someMethod, args=(exampleProxy,)) p.start() 

Sử dụng nó trong quá trình khác như bạn sẽ sử dụng các đối tượng gốc (ví dụ):

def someMethod(exampleProxy): 
    ... 
    exampleProxy.originalExampleMethod() 
    ... 

Nhưng bạn phải lắng nghe nó trong quá trình chính:

exampleProxyListener.listen() 

Đọc thêm và tìm các ví dụ tại đây:

http://matkodjipalo.com/index.php/2017/11/12/proxy-solution-python-multiprocessing/

Các vấn đề liên quan