2016-12-05 7 views
10

Этот код отлично работает на Linux, но не работает под Windows (что ожидается). Я знаю, что модуль многопроцессорности использует fork(), чтобы создать новый процесс, и файловые дескрипторы, принадлежащие родительскому элементу (то есть открытый сокет), поэтому унаследованы дочерним элементом. Тем не менее, я понимаю, что единственный тип данных, которые вы можете отправлять посредством многопроцессорной обработки, должен быть разборчивым. В Windows и Linux объект сокета не является подходящим.Почему Linux может принимать сокеты в многопроцессорном режиме?

from socket import socket, AF_INET, SOCK_STREAM 
import multiprocessing as mp 
import pickle 

sock = socket(AF_INET, SOCK_STREAM) 
sock.connect(("www.python.org", 80)) 
sock.sendall(b"GET/HTTP/1.1\r\nHost: www.python.org\r\n\r\n") 

try: 
    pickle.dumps(sock) 
except TypeError: 
    print("sock is not pickleable") 

def foo(obj): 
    print("Received: {}".format(type(obj))) 
    data, done = [], False 
    while not done: 
     tmp = obj.recv(1024) 
     done = len(tmp) < 1024 
     data.append(tmp) 
    data = b"".join(data) 
    print(data.decode()) 


proc = mp.Process(target=foo, args=(sock,)) 
proc.start() 
proc.join() 

Мой вопрос, почему может сделать socket объект, очевидно, не pickleable объект передается с использованием многопроцессорной? Разве это не использует рассол, как делает Windows?

+4

Дескрипторы файлов не отправляются, они просто там. –

+0

Что это значит? Я мог быть (и должен быть частично) ошибочным, но я думал, что что-либо в параметре «args» должно быть разборчивым. Разве это не так? – Goodies

+1

Аргументы не маринованные, они просто передаются функции в подпроцессе. Необходимо передавать только объекты, передаваемые * между * процессами. –

ответ

6

На платформах UNIX розеток и других файловых дескрипторов могут быть отправлены в другой процесс, использующий доменные UNIX (AF_UNIX) розетки, так что гнезда могут быть маринованные в контексте многопроцессорной.

Модуль многопроцессорной обработки использует специальный экземпляр сортировочного устройства вместо обычного сортировочного устройства, ForkingPickler, для разбора сокетов и дескрипторов файлов, которые затем могут быть разбросаны в другом процессе. Это можно сделать только потому, что известно, что маринованный экземпляр будет разбросан, не имеет смысла рассортировать сокет или файловый дескриптор и отправлять его между границами машины.

Для окон есть similar mechanisms для открытых файлов.

1

Я думаю, что проблема заключается в том, что multiprocessing использует другой сортировщик для систем Windows и других ОС. В Windows нет реального fork(), а травление, которое сделано, эквивалентно травлению через границы машины (то есть распределенные вычисления). В системах, отличных от Windows, объекты (например, дескрипторы файлов) могут быть разделены между границами процесса. Таким образом, травление в системах Windows (с pickle) является более ограниченным.

Пакет multiprocessing действительно использует copy_reg зарегистрировать несколько типов объектов для pickle, и один из этих типов является socket. Однако сериализация объекта socket, используемого в Windows, более ограничена из-за слабой разборчивости Windows.

На соответствующую записку, если вы хотите отправить socket объект с multiprocessing на Windows, вы можете ... вы просто должны использовать пакет multiprocess, который использует dill вместо pickle. dill имеет лучший сериализатор, который может набирать объекты socket на любой ОС, и, таким образом, отправка socket объекта с multiprocess работает в любом случае.

dill имеет функцию copy; по существу loads(dumps(object)) - который полезен для проверки объекта, может быть сериализован. dill также имеет check, который выполняет copy, но с более ограничительной вилкой типа «Windows». Это позволяет пользователям в системах, отличных от Windows, эмулировать copy в системе Windows или через распределенные ресурсы.

>>> import dill 
>>> import socket 
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
>>> s.connect(('www.python.org', 80)) 
>>> s.sendall(b'GET/HTTP/1.1\rnHost: www.python.org\r\n\r\n') 
>>> 
>>> dill.copy(s) 
<socket._socketobject object at 0x10e55b9f0> 
>>> dill.check(s) 
<socket._socketobject object at 0x1059628a0> 
>>> 

Короче говоря, разница обусловлена ​​Pickler, что multiprocessing использует на Windows, отличаться от других, чем Pickler он использует в системах, отличных от Windows. Тем не менее, можно (и легко) работать на любой ОС, используя лучший сериализатор (как это используется в multiprocess).

+1

полное раскрытие: я автор «многопроцесса» и «укроп». –

+0

Очень интересный модуль, укроп! – Goodies