web-dev-qa-db-fra.com

multiprocessing.Pool - PicklingError: Can't pickle <type 'thread.lock'>: la recherche de l'attribut thread.lock a échoué

multiprocessing.Pool Me rend fou ...
Je souhaite mettre à niveau de nombreux packages, et pour chacun d'entre eux, je dois vérifier s'il existe une version supérieure ou non. Cela se fait par la fonction check_one.
Le code principal est dans la méthode Updater.update: Là je crée l'objet Pool et j'appelle la méthode map().

Voici le code:

def check_one(args):
    res, total, package, version = args
    i = res.qsize()
    logger.info('\r[{0:.1%} - {1}, {2} / {3}]',
        i / float(total), package, i, total, addn=False)
    try:
        json = PyPIJson(package).retrieve()
        new_version = Version(json['info']['version'])
    except Exception as e:
        logger.error('Error: Failed to fetch data for {0} ({1})', package, e)
        return
    if new_version > version:
        res.put_nowait((package, version, new_version, json))

class Updater(FileManager):

    # __init__ and other methods...

    def update(self):    
        logger.info('Searching for updates')
        packages = Queue.Queue()
        data = ((packages, self.set_len, dist.project_name, Version(dist.version)) \
            for dist in self.working_set)
        pool = multiprocessing.Pool()
        pool.map(check_one, data)
        pool.close()
        pool.join()
        while True:
            try:
                package, version, new_version, json = packages.get_nowait()
            except Queue.Empty:
                break
            txt = 'A new release is avaiable for {0}: {1!s} (old {2}), update'.format(package,
                                                                                      new_version,
                                                                                      version)
            u = logger.ask(txt, bool=('upgrade version', 'keep working version'), dont_ask=self.yes)
            if u:
                self.upgrade(package, json, new_version)
            else:
                logger.info('{0} has not been upgraded', package)
        self._clean()
        logger.success('Updating finished successfully')

Quand je l'exécute, j'obtiens cette erreur étrange:

Searching for updates
Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 505, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/local/lib/python2.7/dist-packages/multiprocessing/pool.py", line 225, in _handle_tasks
    put(task)
PicklingError: Can't pickle <type 'thread.lock'>: attribute lookup thread.lock failed
31
rubik

le multitraitement passe des tâches (qui incluent check_one et data) aux processus de travail via un mp.SimpleQueue. Contrairement à Queue.Queues, tout est mis dans le mp.SimpleQueue doit être sélectionnable. Queue.Queues ne sont pas sélectionnables:

import multiprocessing as mp
import Queue

def foo(queue):
    pass

pool=mp.Pool()
q=Queue.Queue()

pool.map(foo,(q,))

donne cette exception:

UnpickleableError: Cannot pickle <type 'thread.lock'> objects

Votre data comprend packages, qui est une file d'attente. Cela pourrait être la source du problème.


Voici une solution de contournement possible: Queue est utilisé à deux fins:

  1. pour connaître la taille approximative (en appelant qsize)
  2. pour stocker les résultats pour une récupération ultérieure.

Au lieu d'appeler qsize, pour partager une valeur entre plusieurs processus, nous pourrions utiliser un mp.Value.

Au lieu de stocker les résultats dans une file d'attente, nous pouvons (et devons) simplement renvoyer les valeurs des appels à check_one. Le pool.map recueille les résultats dans une file d'attente de sa propre création et renvoie les résultats en tant que valeur de retour de pool.map.

Par exemple:

import multiprocessing as mp
import Queue
import random
import logging

# logger=mp.log_to_stderr(logging.DEBUG)
logger = logging.getLogger(__name__)


qsize = mp.Value('i', 1)
def check_one(args):
    total, package, version = args
    i = qsize.value
    logger.info('\r[{0:.1%} - {1}, {2} / {3}]'.format(
        i / float(total), package, i, total))
    new_version = random.randrange(0,100)
    qsize.value += 1
    if new_version > version:
        return (package, version, new_version, None)
    else:
        return None

def update():    
    logger.info('Searching for updates')
    set_len=10
    data = ( (set_len, 'project-{0}'.format(i), random.randrange(0,100))
             for i in range(set_len) )
    pool = mp.Pool()
    results = pool.map(check_one, data)
    pool.close()
    pool.join()
    for result in results:
        if result is None: continue
        package, version, new_version, json = result
        txt = 'A new release is avaiable for {0}: {1!s} (old {2}), update'.format(
            package, new_version, version)
        logger.info(txt)
    logger.info('Updating finished successfully')

if __name__=='__main__':
    logging.basicConfig(level=logging.DEBUG)
    update()
28
unutbu

Après avoir beaucoup creusé sur un problème similaire ...

Il s'avère également que TOUT objet contenant un objet threading.Condition () ne fonctionnera JAMAIS avec le multiprocessing.Pool.

Voici un exemple

import multiprocessing as mp
import threading

class MyClass(object):
   def __init__(self):
      self.cond = threading.Condition()

def foo(mc):
   pass

pool=mp.Pool()
mc=MyClass()
pool.map(foo,(mc,))

Je l'ai exécuté avec Python 2.7.5 et j'ai rencontré la même erreur:

Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib64/python2.7/threading.py", line 811, in __bootstrap_inner
self.run()
  File "/usr/lib64/python2.7/threading.py", line 764, in run
self.__target(*self.__args, **self.__kwargs)
  File "/usr/lib64/python2.7/multiprocessing/pool.py", line 342, in _handle_tasks
put(task)
PicklingError: Can't pickle <type 'thread.lock'>: attribute lookup thread.lock failed

Mais ensuite, je l'ai exécuté sur python 3.4.1 et ce problème a été corrigé.

Bien que je n'aie pas encore trouvé de solutions de contournement utiles pour ceux d'entre nous qui sont encore sur 2.7.x.

7
jersey bean

J'ai rencontré ce problème avec python version 3.6 sur docker. Changé la version en 3.7.3 et il a été résolu.

1