2012-02-24 33 views
37

tôi có một mối quan tâm về multiprocessing.Manager() trong python, đây là ví dụ này,Làm thế nào để multiprocessing.Manager() làm việc trong python?

import multiprocessing 

def f(ns): 

    ns.x *=10 
    ns.y *= 10 

if __name__ == '__main__': 
    manager = multiprocessing.Manager() 
    ns = manager.Namespace() 
    ns.x = 1 
    ns.y = 2 

    print 'before', ns 
    p = multiprocessing.Process(target=f, args=(ns,)) 
    p.start() 
    p.join() 
    print 'after', ns 

và đầu ra là,

before Namespace(x=1, y=2) 
after Namespace(x=10, y=20) 

Đến nay, nó đã làm việc như mong đợi của tôi, sau đó tôi sửa đổi mã như thế này,

import multiprocessing 

def f(ns): 

    ns.x.append(10) 
    ns.y.append(10) 

if __name__ == '__main__': 
    manager = multiprocessing.Manager() 
    ns = manager.Namespace() 
    ns.x = [] 
    ns.y = [] 

    print 'before', ns 
    p = multiprocessing.Process(target=f, args=(ns,)) 
    p.start() 
    p.join() 
    print 'after', ns 

bây giờ, đầu ra là,

before Namespace(x=[], y=[]) 
after Namespace(x=[], y=[]) 

Nó làm tôi bối rối vì sao danh sách không thay đổi như mong đợi của tôi? ai cũng có thể giúp tôi tìm ra chuyện gì đã xảy ra? Cảm ơn trước!

Trả lời

40

Lưu ý: Câu trả lời này có thể hoặc không chính xác cho Python 3.6+. Xem nhận xét bên dưới, điều này dường như chỉ ra những điều mâu thuẫn. Tôi sẽ cập nhật khi tôi có thời gian để điều tra. (Hãy để chỉnh sửa câu trả lời này nếu bạn biết những gì đang xảy ra!) Đối tượng


Giám đốc ủy quyền không có khả năng tuyên truyền thay đổi được thực để đối tượng có thể thay đổi bên trong một container. Nói cách khác, nếu bạn có đối tượng manager.list(), mọi thay đổi đối với danh sách được quản lý sẽ được truyền cho tất cả các quy trình khác. Nhưng nếu bạn có danh sách bên trong danh sách đó, mọi thay đổi đối với danh sách bên trong sẽ không được truyền, vì người quản lý không có cách nào phát hiện thay đổi.

Để truyền các thay đổi, bạn phải sửa đổi đối tượng manager.list() trực tiếp, như được chỉ rõ trong ghi chú here.

Ví dụ, hãy xem xét đoạn mã sau và đầu ra của nó:

import multiprocessing 
import time 

def f(ns, ls, di): 
    ns.x += 1 
    ns.y[0] += 1 
    ns_z = ns.z 
    ns_z[0] += 1 
    ns.z = ns_z 

    ls[0] += 1 
    ls[1][0] += 1 
    ls_2 = ls[2] 
    ls_2[0] += 1 
    ls[2] = ls_2 

    di[0] += 1 
    di[1][0] += 1 
    di_2 = di[2] 
    di_2[0] += 1 
    di[2] = di_2 

if __name__ == '__main__': 
    manager = multiprocessing.Manager() 
    ns = manager.Namespace() 
    ns.x = 1 
    ns.y = [1] 
    ns.z = [1] 
    ls = manager.list([1, [1], [1]]) 
    di = manager.dict({0: 1, 1: [1], 2:[1]}) 

    print 'before', ns, ls, di 
    p = multiprocessing.Process(target=f, args=(ns, ls, di)) 
    p.start() 
    p.join() 
    print 'after', ns, ls, di 

Output:

before Namespace(x=1, y=[1], z=[1]) [1, [1], [1]] {0: 1, 1: [1], 2: [1]} 
after Namespace(x=2, y=[1], z=[2]) [2, [1], [2]] {0: 2, 1: [1], 2: [2]} 

Như bạn có thể thấy, khi một giá trị mới được gán trực tiếp vào container được quản lý, nó thay đổi ; khi được gán cho vùng chứa có thể thay đổi trong vùng chứa được quản lý, vùng chứa sẽ không thay đổi; nhưng nếu vùng chứa có thể thay đổi sau đó được gán lại cho vùng chứa được quản lý, nó sẽ thay đổi lại.

+3

Bắt đầu với 3.6, thay đổi đối với các đối tượng lồng nhau được truyền tự động. – max

+0

Tôi đã gặp phải một số vấn đề khi sử dụng từ điển lồng nhau trong một NameSpace với Trình quản lý trên Python 3.6.4. Hãy chắc chắn rằng các đối tượng lồng nhau của bạn đang được cập nhật đúng cách trước khi tiến lên phía trước. Giải pháp cho tôi là xác định rõ ràng từng đối tượng được chia sẻ như một đối tượng Manager. – Joules

16

ns là trường hợp NamespaceProxy. Các đối tượng này có các phương thức đặc biệt __getattr__, __setattr____delattr__ cho phép chia sẻ giá trị giữa các quy trình. Để tận dụng cơ chế này khi thay đổi giá trị, bạn phải kích hoạt __setattr__.

ns.x.append(10) 

gây ns.__getattr__ được gọi để lấy ns.x, nhưng nó không gây ns.__setattr__ được gọi.

Để khắc phục điều này, bạn phải sử dụng ns.x = ....

def f(ns): 
    tmp = ns.x  # retrieve the shared value 
    tmp.append(10) 
    ns.x = tmp  # set the shared value 
Các vấn đề liên quan