web-dev-qa-db-fra.com

les mises à jour des variables globales de multi-traitement ne sont pas retournées au parent

J'essaie de renvoyer des valeurs à partir de sous-processus, mais ces valeurs ne sont malheureusement pas récupérables. J'ai donc utilisé avec succès des variables globales dans le module des threads, mais je n'ai pas pu récupérer les mises à jour effectuées dans les sous-processus lors de l'utilisation du module de multitraitement. J'espère que je manque quelque chose.

Les résultats imprimés à la fin sont toujours les mêmes que les valeurs initiales étant donné les vars dataDV03 et dataDV04. Les sous-processus mettent à jour ces variables globales mais ces variables globales restent inchangées dans le parent.

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

Je ne peux pas poster ma question, j'essaierai donc de modifier l'original.

Voici l'objet qui n'est pas picklable:

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

C'est l'erreur renvoyée lorsque j'utilise un multiprocessing.Pool pour renvoyer l'instance au parent:

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]:
33
Buoy

Lorsque vous utilisez multiprocessing pour ouvrir un deuxième processus, une instance entièrement nouvelle de Python, avec son propre état global, est créée. Cet état global n'est pas partagé, donc les modifications apportées par les processus enfants aux variables globales seront invisibles pour le processus parent.

De plus, la plupart des abstractions que multiprocessing fournit utilisent des cornichons pour transférer des données. Toutes les données transférées à l'aide de procurations doivent être récupérables ; qui inclut tous les objets fournis par Manager . Citations pertinentes (c'est moi qui souligne):

Assurez-vous que les arguments des méthodes des proxys sont picklables.

Et (dans la section Manager):

D'autres processus peuvent accéder aux objets partagés à l'aide de proxys .

Queues nécessitent également des données picklables; les documents ne le disent pas, mais un test rapide le confirme:

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()

Production:

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

La seule approche qui pourrait fonctionner pour vous, si vous ne pouvez vraiment pas décaper les données, est de trouver un moyen de les stocker en tant que ctype objet; une référence à la mémoire peut alors être transmise à un processus enfant . Cela me semble assez douteux; Je ne l'ai jamais fait. Mais cela pourrait être une solution possible pour vous.

Compte tenu de votre mise à jour, il semble que vous ayez besoin d'en savoir plus sur les composants internes d'un LORR. LORR est-il une classe? Pouvez-vous en sous-classer? Est-ce une sous-classe d'autre chose? Quel est son MRO? (Essayez LORR.__mro__ et publier la sortie si cela fonctionne.) S'il s'agit d'un pur objet python, il peut être possible de le sous-classer, créant un __setstate__ et un __getstate__ pour activer le décapage.

Une autre approche pourrait être de comprendre comment extraire les données pertinentes d'une instance LORR et les transmettre via une simple chaîne. Puisque vous dites que vous voulez vraiment simplement appeler les méthodes de l'objet, pourquoi ne pas le faire en utilisant Queues pour envoyer des messages dans les deux sens? En d'autres termes, quelque chose comme ça (schématiquement):

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
35
senderle

@DBlas vous donne une URL rapide et une référence à la classe Manager dans une réponse, mais je pense que c'est toujours un peu vague, donc j'ai pensé qu'il pourrait être utile pour vous de le voir appliqué ...

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

Étant donné que le multitraitement utilise en fait des processus distincts, vous ne pouvez pas simplement partager des variables globales car elles se trouveront dans des "espaces" complètement différents en mémoire. Ce que vous faites à un global dans le cadre d'un processus ne se reflétera pas dans un autre. Bien que j'admette que cela semble déroutant depuis la façon dont vous le voyez, il vit tous là dans le même morceau de code, alors "pourquoi ces méthodes ne devraient-elles pas avoir accès au global"? Il est plus difficile d'envelopper votre tête autour de l'idée qu'ils s'exécuteront dans différents processus.

La classe Manager est donnée pour agir en tant que proxy pour les structures de données qui peuvent faire circuler les informations dans les deux sens entre les processus. Ce que vous allez faire est de créer un dict et une liste spéciaux à partir d'un gestionnaire, de les transmettre à vos méthodes et de les exploiter localement.

Données non décapables

Pour votre objet LORR spécialisé, vous devrez peut-être créer quelque chose comme un proxy qui peut représenter l'état sélectionnable de l'instance.

Pas super robuste ou testé beaucoup, mais vous donne l'idée.

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
6
jdi

Lorsque vous utilisez multiprocess, la seule façon de passer des objets entre les processus est d'utiliser Queue ou Pipe; les globaux ne sont pas partagés. Les objets doivent être décapables, donc multiprocess ne vous aidera pas ici.

5
ecatmur

Vous pouvez également utiliser un tableau multiprocesseur . Cela vous permet d'avoir un état partagé entre les processus et est probablement la chose la plus proche d'une variable globale.

En haut de main, déclarez un tableau. Le premier argument "i" indique que ce seront des entiers. Le deuxième argument donne les valeurs initiales:

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

Passez ensuite ce tableau au processus comme argument:

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

Vous devez recevoir l'argument tableau dans la fonction appelée, puis vous pouvez le modifier dans la fonction:

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

Le tableau est partagé avec le parent, vous pouvez donc imprimer les valeurs à la fin dans le parent:

print 'DV03', shared_dataDV03[:]

Et cela montrera les changements:

DV03 [0, 1]
4
Paul

J'utilise p.map () pour créer un certain nombre de processus sur des serveurs distants et imprimer les résultats lorsqu'ils reviennent à des moments imprévisibles:

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

Cela fonctionnait bien si DoIndividualSummary utilisait print pour les résultats, mais le résultat global était dans un ordre imprévisible, ce qui rendait l'interprétation difficile. J'ai essayé un certain nombre d'approches pour utiliser des variables globales mais j'ai rencontré des problèmes. Enfin, j'ai réussi avec sqlite3.

Avant p.map(), ouvrez une connexion sqlite et créez une table:

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()

Ensuite, lors du retour de DoIndividualSummary(), enregistrez les résultats dans le tableau:

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

Après l'instruction map(), imprimez les résultats:

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

Cela peut sembler exagéré, mais c'était plus simple pour moi que les solutions recommandées.

1
Eva Smith