многопроцессорные обновления глобальных переменных не возвращаются родителю

Я пытаюсь вернуть значения из подпроцессов, но эти значения, к сожалению, не выбираются. Поэтому я успешно использовал глобальные переменные в модуле потоков, но не смог получить обновления, сделанные в подпроцессах при использовании многопроцессорного модуля. Я надеюсь, что что-то упустил.

Результаты, напечатанные в конце, всегда совпадают с начальными значениями, заданными переменными dataDV03 и dataDV04. Подпроцессы обновляют эти глобальные переменные, но эти глобальные переменные остаются неизменными в родительском.

import multiprocessing

# NOT ABLE to get python to return values in passed variables.

ants = ['DV03', 'DV04']
dataDV03 = ['', '']
dataDV04 = {'driver': '', 'status': ''}


def getDV03CclDrivers(lib):  # call global variable
    global dataDV03
    dataDV03[1] = 1
    dataDV03[0] = 0

# eval( 'CCL.' + lib + '.' +  lib + '( "DV03" )' ) these are unpicklable instantiations

def getDV04CclDrivers(lib, dataDV04):   # pass global variable
    dataDV04['driver'] = 0  # eval( 'CCL.' + lib + '.' +  lib + '( "DV04" )' )


if __name__ == "__main__":

    jobs = []
    if 'DV03' in ants:
        j = multiprocessing.Process(target=getDV03CclDrivers, args=('LORR',))
        jobs.append(j)

    if 'DV04' in ants:
        j = multiprocessing.Process(target=getDV04CclDrivers, args=('LORR', dataDV04))
        jobs.append(j)

    for j in jobs:
        j.start()

    for j in jobs:
        j.join()

    print 'Results:\n'
    print 'DV03', dataDV03
    print 'DV04', dataDV04

Я не могу опубликовать свой вопрос, поэтому постараюсь отредактировать оригинал.

Вот объект, который нельзя отобрать:

In [1]: from CCL import LORR
In [2]: lorr=LORR.LORR('DV20', None)
In [3]: lorr
Out[3]: <CCL.LORR.LORR instance at 0x94b188c>

Эта ошибка возвращается, когда я использую multiprocessing.Pool для возврата экземпляра родителю:

Thread getCcl (('DV20', 'LORR'),)
Process PoolWorker-1:
Traceback (most recent call last):
File "/alma/ACS-10.1/casa/lib/python2.6/multiprocessing/process.py", line 232, in _bootstrap
self.run()
File "/alma/ACS-10.1/casa/lib/python2.6/multiprocessing/process.py", line 88, in run
self._target(*self._args, **self._kwargs)
File "/alma/ACS-10.1/casa/lib/python2.6/multiprocessing/pool.py", line 71, in worker
put((job, i, result))
File "/alma/ACS-10.1/casa/lib/python2.6/multiprocessing/queues.py", line 366, in put
return send(obj)
UnpickleableError: Cannot pickle <type 'thread.lock'> objects
In [5]: dir(lorr)
Out[5]:
['GET_AMBIENT_TEMPERATURE',
 'GET_CAN_ERROR',
 'GET_CAN_ERROR_COUNT',
 'GET_CHANNEL_NUMBER',
 'GET_COUNT_PER_C_OP',
 'GET_COUNT_REMAINING_OP',
 'GET_DCM_LOCKED',
 'GET_EFC_125_MHZ',
 'GET_EFC_COMB_LINE_PLL',
 'GET_ERROR_CODE_LAST_CAN_ERROR',
 'GET_INTERNAL_SLAVE_ERROR_CODE',
 'GET_MAGNITUDE_CELSIUS_OP',
 'GET_MAJOR_REV_LEVEL',
 'GET_MINOR_REV_LEVEL',
 'GET_MODULE_CODES_CDAY',
 'GET_MODULE_CODES_CMONTH',
 'GET_MODULE_CODES_DIG1',
 'GET_MODULE_CODES_DIG2',
 'GET_MODULE_CODES_DIG4',
 'GET_MODULE_CODES_DIG6',
 'GET_MODULE_CODES_SERIAL',
 'GET_MODULE_CODES_VERSION_MAJOR',
 'GET_MODULE_CODES_VERSION_MINOR',
 'GET_MODULE_CODES_YEAR',
 'GET_NODE_ADDRESS',
 'GET_OPTICAL_POWER_OFF',
 'GET_OUTPUT_125MHZ_LOCKED',
 'GET_OUTPUT_2GHZ_LOCKED',
 'GET_PATCH_LEVEL',
 'GET_POWER_SUPPLY_12V_NOT_OK',
 'GET_POWER_SUPPLY_15V_NOT_OK',
 'GET_PROTOCOL_MAJOR_REV_LEVEL',
 'GET_PROTOCOL_MINOR_REV_LEVEL',
 'GET_PROTOCOL_PATCH_LEVEL',
 'GET_PROTOCOL_REV_LEVEL',
 'GET_PWR_125_MHZ',
 'GET_PWR_25_MHZ',
 'GET_PWR_2_GHZ',
 'GET_READ_MODULE_CODES',
 'GET_RX_OPT_PWR',
 'GET_SERIAL_NUMBER',
 'GET_SIGN_OP',
 'GET_STATUS',
 'GET_SW_REV_LEVEL',
 'GET_TE_LENGTH',
 'GET_TE_LONG_FLAG_SET',
 'GET_TE_OFFSET_COUNTER',
 'GET_TE_SHORT_FLAG_SET',
 'GET_TRANS_NUM',
 'GET_VDC_12',
 'GET_VDC_15',
 'GET_VDC_7',
 'GET_VDC_MINUS_7',
 'SET_CLEAR_FLAGS',
 'SET_FPGA_LOGIC_RESET',
 'SET_RESET_AMBSI',
 'SET_RESET_DEVICE',
 'SET_RESYNC_TE',
 'STATUS',
 '_HardwareDevice__componentName',
 '_HardwareDevice__hw',
 '_HardwareDevice__stickyFlag',
 '_LORRBase__logger',
 '__del__',
 '__doc__',
 '__init__',
 '__module__',
 '_devices',
 'clearDeviceCommunicationErrorAlarm',
 'getControlList',
 'getDeviceCommunicationErrorCounter',
 'getErrorMessage',
 'getHwState',
 'getInternalSlaveCanErrorMsg',
 'getLastCanErrorMsg',
 'getMonitorList',
 'hwConfigure',
 'hwDiagnostic',
 'hwInitialize',
 'hwOperational',
 'hwSimulation',
 'hwStart',
 'hwStop',
 'inErrorState',
 'isMonitoring',
 'isSimulated']

In [6]:

Ответ 1

Когда вы используете multiprocessing для открытия второго процесса, создается совершенно новый экземпляр Python со своим собственным глобальным состоянием. Это глобальное состояние не разделяется, поэтому изменения, выполняемые дочерними процессами для глобальных переменных, будут невидимы для родительского процесса.

Кроме того, большинство абстракций, которые multiprocessing обеспечивают использование pickle для передачи данных. Все данные, переданные с использованием прокси должны быть разборчивыми; который включает в себя все объекты, которые предоставляет Manager. Соответствующие цитаты (мой акцент):

Убедитесь в том, что аргументы методов прокси-сервера выделены.

И (в разделе Manager):

Другие процессы могут обращаться к общим объектам с помощью прокси.

Queue также требуют отборных данных; документы не говорят этого, но быстрый тест подтверждает это:

import multiprocessing
import pickle

class Thing(object):
    def __getstate__(self):
        print 'got pickled'
        return self.__dict__
    def __setstate__(self, state):
        print 'got unpickled'
        self.__dict__.update(state)

q = multiprocessing.Queue()
p = multiprocessing.Process(target=q.put, args=(Thing(),))
p.start()
print q.get()
p.join()

Вывод:

$ python mp.py 
got pickled
got unpickled
<__main__.Thing object at 0x10056b350>

Один подход, который может сработать для вас, если вы действительно не можете разборки данных, заключается в том, чтобы найти способ сохранить его как объект ctype; ссылка на память может быть передана дочернему процессу. Это кажется мне довольно изворотливым; Я никогда этого не делал. Но это может быть возможным решением для вас.

Учитывая ваше обновление, кажется, вам нужно знать намного больше о внутренних элементах LORR. Является ли LORR классом? Можете ли вы подкласс от него? Является ли это подклассом чего-то еще? Что его MRO? (Попробуйте LORR.__mro__ и опубликуйте вывод, если он работает.) Если это чистый объект python, возможно, его можно подклассифицировать, создав __setstate__ и a __getstate__, чтобы включить травление.

Другой подход может заключаться в том, чтобы выяснить, как получить соответствующие данные из экземпляра LORR и передать его через простую строку. Поскольку вы говорите, что вы действительно просто хотите вызвать методы объекта, почему бы просто не сделать это, используя Queue для отправки сообщений туда и обратно? Другими словами, что-то вроде этого (схематично):

Main Process              Child 1                       Child 2
                          LORR 1                        LORR 2 
child1_in_queue     ->    get message 'foo'
                          call 'foo' method
child1_out_queue    <-    return foo data string
child2_in_queue                   ->                    get message 'bar'
                                                        call 'bar' method
child2_out_queue                  <-                    return bar data string

Ответ 2

@DBlas дает вам быстрый URL-адрес и ссылку на класс Manager в ответе, но я думаю, что он все еще немного расплывчатый, поэтому я подумал, что вам может быть полезно просто увидеть его применимым...

import multiprocessing
from multiprocessing import Manager

ants = ['DV03', 'DV04']

def getDV03CclDrivers(lib, data_dict):  
    data_dict[1] = 1
    data_dict[0] = 0

def getDV04CclDrivers(lib, data_list):   
    data_list['driver'] = 0  


if __name__ == "__main__":

    manager = Manager()
    dataDV03 = manager.list(['', ''])
    dataDV04 = manager.dict({'driver': '', 'status': ''})

    jobs = []
    if 'DV03' in ants:
        j = multiprocessing.Process(
                target=getDV03CclDrivers, 
                args=('LORR', dataDV03))
        jobs.append(j)

    if 'DV04' in ants:
        j = multiprocessing.Process(
                target=getDV04CclDrivers, 
                args=('LORR', dataDV04))
        jobs.append(j)

    for j in jobs:
        j.start()

    for j in jobs:
        j.join()

    print 'Results:\n'
    print 'DV03', dataDV03
    print 'DV04', dataDV04

Поскольку многопроцессорная обработка на самом деле использует отдельные процессы, вы не можете просто совместно использовать глобальные переменные, потому что они будут находиться в совершенно разных "пространствах" в памяти. То, что вы делаете с глобальным в рамках одного процесса, не будет отражаться в другом. Хотя я признаю, что это кажется странным, так как вы видите его, все живёт прямо там, в одном и том же куске кода, поэтому "почему эти методы не должны иметь доступ к глобальному"? Труднее понять, что они будут работать в разных процессах.

Класс Manager предназначен для использования в качестве прокси для структур данных, которые могут перемещать информацию между вами и обратно между процессами. То, что вы будете делать, - это создать специальный дикт и список из менеджера, передать их в ваши методы и работать с ними локально.

Un-маринованные данные

Для вашего специализированного объекта LORR вам может понадобиться создать что-то вроде прокси, которое может представлять выбираемое состояние экземпляра.

Не супер надежный или проверенный много, но дает вам идею.

class LORRProxy(object):

    def __init__(self, lorrObject=None):
        self.instance = lorrObject

    def __getstate__(self):
        # how to get the state data out of a lorr instance
        inst = self.instance
        state = dict(
            foo = inst.a,
            bar = inst.b,
        )
        return state

    def __setstate__(self, state):
        # rebuilt a lorr instance from state
        lorr = LORR.LORR()
        lorr.a = state['foo']
        lorr.b = state['bar']
        self.instance = lorr

Ответ 3

При использовании multiprocess единственный способ передать объекты между процессами - использовать Queue или Pipe; globals не разделяются. Объекты должны быть разборчивы, поэтому multiprocess вам не поможет.

Ответ 4

Вы также можете использовать массив многопроцессорных массивов. Это позволяет вам иметь общее состояние между процессами и, вероятно, самое близкое к глобальной переменной.

В верхней части main объявите массив. Первый аргумент "i" говорит, что он будет целым числом. Второй аргумент дает начальные значения:

shared_dataDV03 = multiprocessing.Array ('i', (0, 0)) #a shared array

Затем передайте этот массив процессу в качестве аргумента:

j = multiprocessing.Process(target=getDV03CclDrivers, args=('LORR',shared_dataDV03))

Вы должны получить аргумент массива в вызываемой функции, а затем вы можете изменить его внутри функции:

def getDV03CclDrivers(lib,arr):  # call global variable
    arr[1]=1
    arr[0]=0

Массив разделяется с родителем, поэтому вы можете распечатать значения в конце родителя:

print 'DV03', shared_dataDV03[:]

И он покажет изменения:

DV03 [0, 1]

Ответ 5

Я использую p.map() для выделения нескольких процессов на удаленные серверы и распечатываю результаты, когда они возвращаются в непредсказуемое время:

Servers=[...]
from multiprocessing import Pool
p=Pool(len(Servers))
p.map(DoIndividualSummary, Servers)

Это отлично работало, если DoIndividualSummary использовал print для результатов, но общий результат был в непредсказуемом порядке, что затрудняло интерпретацию. Я попытался использовать несколько подходов для использования глобальных переменных, но столкнулся с проблемами. Наконец, мне удалось с sqlite3.

До p.map() откройте соединение sqlite и создайте таблицу:

import sqlite3
conn=sqlite3.connect('servers.db') # need conn for commit and close
db=conn.cursor()
try: db.execute('''drop table servers''')
except: pass
db.execute('''CREATE TABLE servers (server text, serverdetail text, readings     text)''')
conn.commit()

Затем, возвращаясь из DoIndividualSummary(), сохраните результаты в таблице:

db.execute('''INSERT INTO servers VALUES (?,?,?)''',         (server,serverdetail,readings))
conn.commit()
return

После инструкции map() напечатайте результаты:

db.execute('''select * from servers order by server''')
rows=db.fetchall()
for server,serverdetail,readings in rows: print serverdetail,readings

Может показаться излишним, но для меня это было проще, чем рекомендуемые решения.