2014-05-13 16 views
6

Tôi đã viết một chương trình như thế này:Đa danh sách chia sẻ

from multiprocessing import Process, Manager 

def worker(i): 
    x[i].append(i) 

if __name__ == '__main__': 
    manager = Manager() 
    x = manager.list() 
    for i in range(5): 
     x.append([]) 
    p = [] 
    for i in range(5): 
     p.append(Process(target=worker, args=(i,))) 
     p[i].start() 

    for i in range(5): 
     p[i].join() 

    print x 

Tôi muốn tạo ra một danh sách chia sẻ các danh sách trong quy trình và mỗi quá trình sửa đổi một danh sách trong đó. Nhưng kết quả của chương trình này là danh sách các danh sách trống: [[], [], [], [], []].

Có gì không ổn?

+0

khi bạn gỡ lỗi nó, bạn có thể thiết lập một breakpoint trong việc def (i)? Điều đó có thể không được gọi là – linpingta

+0

@linpingta Tôi đã thêm tuyên bố in trong công nhân (i) và thông báo được in ra. –

+0

Bạn có chắc chắn trong 'worker' mà bạn muốn nối thêm vào' x [i] 'chứ không phải' x' (toàn cầu) không? – beroe

Trả lời

6

Tôi nghĩ điều này là do quirk trong cách Người quản lý được triển khai.

Nếu bạn tạo ra hai đối tượng Manager.list, và sau đó thêm một trong những danh sách này để người kia, các loại danh sách mà bạn gắn những thay đổi bên trong danh sách phụ huynh:

>>> type(l) 
<class 'multiprocessing.managers.ListProxy'> 
>>> type(z) 
<class 'multiprocessing.managers.ListProxy'> 
>>> l.append(z) 
>>> type(l[0]) 
<class 'list'> # Not a ListProxy anymore 

l[0]z không cùng một đối tượng và không hoạt động hoàn toàn theo cách bạn mong muốn như sau:

>>> l[0].append("hi") 
>>> print(z) 
[] 
>>> z.append("hi again") 
>>> print(l[0]) 
['hi again'] 

Như bạn có thể thấy, việc thay đổi danh sách lồng nhau không ảnh hưởng đến đối tượng ListProxy, nhưng thay đổi Đối tượng ListProxy thay đổi các nes ted danh sách. Các tài liệu thực sự explicitly notes this:

Note

Sửa đổi các giá trị có thể thay đổi hoặc các mục trong dict và danh sách các proxy sẽ không được tuyên truyền thông qua người quản lý, bởi vì proxy không có cách nào biết khi nào giá trị hoặc các mục của nó được sửa đổi. Để sửa đổi như một mục, bạn có thể tái gán đối tượng sửa đổi để proxy container:

Đào thông qua các mã nguồn, bạn có thể thấy rằng khi bạn gọi append trên ListProxy, cuộc gọi append thực sự được gửi cho đối tượng người quản lý qua IPC và sau đó người quản lý gọi thêm vào danh sách được chia sẻ. Điều đó có nghĩa là các số điện thoại đến số append cần được nhận/bỏ tạm dừng. Trong quá trình giải nén, đối tượng ListProxy được chuyển thành một danh sách Python thông thường, đó là một bản sao của những gì mà ListProxy trỏ tới (hay còn gọi là referent của nó). Đây cũng là noted in the documentation:

Một tính năng quan trọng của các đối tượng proxy là chúng có thể được chuyển qua lại giữa các quá trình. Tuy nhiên, lưu ý rằng nếu một proxy được gửi đến quy trình của người quản lý tương ứng thì việc bỏ chọn nó sẽ tự động tự giới thiệu. Điều này có nghĩa, ví dụ, rằng một đối tượng chia sẻ có thể chứa một giây

Vì vậy, sẽ trở lại ví dụ trên, nếu l [0] là một bản sao của z, tại sao không cập nhật z cũng cập nhật l[0]? Bởi vì bản sao cũng được đăng ký với đối tượng Proxy, do đó, khi bạn thay đổi ListProxy (z trong ví dụ trên), nó cũng cập nhật tất cả các bản sao đã đăng ký của danh sách (l[0] trong ví dụ trên). Tuy nhiên, bản sao không biết gì về proxy, vì vậy khi bạn thay đổi bản sao, Proxy sẽ không thay đổi.Vì vậy, để làm ví dụ của bạn hoạt động, bạn cần tạo đối tượng manager.list() mới mỗi khi bạn muốn sửa đổi danh sách phụ và chỉ cập nhật đối tượng proxy đó trực tiếp, thay vì cập nhật đối tượng đó qua chỉ mục của danh sách mẹ :

#!/usr/bin/python 

from multiprocessing import Process, Manager 

def worker(x, i, *args): 
    sub_l = manager.list(x[i]) 
    sub_l.append(i) 
    x[i] = sub_l 


if __name__ == '__main__': 
    manager = Manager() 
    x = manager.list([[]]*5) 
    print x 
    p = [] 
    for i in range(5): 
     p.append(Process(target=worker, args=(x, i))) 
     p[i].start() 

    for i in range(5): 
     p[i].join() 

    print x 

Dưới đây là kết quả:

[email protected]:~$ ./multi_weirdness.py 
[[0], [1], [2], [3], [4]] 
+0

Cảm ơn câu trả lời của bạn! Tôi dự định thay đổi danh sách con thông qua danh sách phụ huynh, nhưng có vẻ như tôi không thể. Ban đầu tôi sử dụng danh sách, nhưng nó làm chậm hoạt động song song của tôi. Có cách nào khác để sử dụng danh sách chia sẻ có thể tránh được sự cố này không? –

+0

Xem ví dụ ở cuối câu trả lời của tôi. Nó cho thấy cách bạn có thể giải quyết vấn đề bằng cách tạo một manager.list mới từ danh sách con, cập nhật đối tượng proxy mới và sau đó chèn proxy trở lại vào danh sách mẹ. – dano

+0

Tôi chạy vào một vấn đề tương tự nhưng cách giải quyết không phải là một tùy chọn khi tạo quá nhiều proxy sẽ mất nhiều thời gian. Có cách nào khác không? –

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