0

je rencontre quelques problèmes à comprendre ce que je passe avec les processus dans le code suivant je l'ai écrit comme une simplification d'un autre script que je vous écris pour se familiariser avec multitraitement:Comment travailler avec la restriction du système d'exploitation sur le nombre maximal de processus dans une fonction de multitraitement récursif?

import multiprocessing 
import time 
from random import randint 
from os import getpid 



def f(i, process_id, parent_process_message_queue): 

    print (i,process_id, getpid()) 
    time.sleep(randint(0,10)/100) 

    child_processes = [] 
    child_process_message_queue = multiprocessing.Queue() 

    if randint(0,10) > 4: 
     child_processes.append(multiprocessing.Process(target = f, args = (i+1, 0, child_process_message_queue))) 
     child_processes[-1].start() 
     child_processes.append(multiprocessing.Process(target = f, args = (i+1, 1, child_process_message_queue))) 
     child_processes[-1].start() 

    while not child_process_message_queue.empty(): 
     child_id = child_process_message_queue.get() 
     child_processes[child_id].join() 
    parent_process_message_queue.put(process_id) 


child_process_message_queue = multiprocessing.Queue() 

p = multiprocessing.Process(target = f, args = (0, 0, child_process_message_queue)) 
p.start() 

while not child_process_message_queue.empty(): 
    child_id = child_process_message_queue.get() 
    child_processes[child_id].join() 

p.join() 

Lorsqu'ils ne sont pas trop récursive les appels arrivent (à cause de l'élément aléatoire) alors ce code semble fonctionner comme prévu.

sortie d'une course réussie ressemble à ceci:

0 0 57756 
1 0 57757 
1 1 57758 
2 0 57759 
2 1 57760 
3 0 57761 
3 1 57762 
3 0 57763 
3 1 57764 
4 0 57765 
4 1 57766 
4 0 57767 
4 1 57768 
5 0 57769 
5 1 57770 

Cependant, lorsque trop d'appels récursifs sont faits, les choses deviennent étranges; des choses comme cela semble, y compris un certain message d'erreur:

Process Process-1:2:1:2:1:2:2:2:2:1:1:1:1:1:1:1:1:2:2:2:2:2:2:1:1:1:2:2:1:1:1:1:2:1:2:1: 
Process Process-1:2:1:2:1:2:2:2:2:1:1:1:1:1:1:1:2:2:2:1:2:2:2:1:2:2:2:1:2:2:2:1:1:2:2:2:1:1:1:1:2: 
Process Process-1:2:1:2:1:2:2:2:2:1:1:1:1:1:1:1:1:2:2:2:2:2:2:2:2:2:2:2:2:2:1:2:2:2:1:2:2:2:1:2: 
Process Process-1:2:1:2:1:2:2:2:2:1:1:1:1:1:1:1:1:2:2:2:1:2:2:2:1:2:1:2:2:1:1:2:2:1:2:2:1:2:1:1:2:1:1: 
Process Process-1:2:1:2:2:2:2:1:2:2:2:1:1:1:2:1:2:2:1:2:2:1:1:2:1:1:1:2:1:1:2:2:1:1:1:1:1:1:1:1:2:1:1:2:2:2:2:2: 
Traceback (most recent call last): 
    File "/Users/user/anaconda/lib/python3.6/multiprocessing/process.py", line 249, in _bootstrap 
    self.run() 
    File "/Users/user/anaconda/lib/python3.6/multiprocessing/process.py", line 93, in run 
    self._target(*self._args, **self._kwargs) 
    File "queuetest2.py", line 18, in f 
    child_processes[-1].start() 
    File "/Users/user/anaconda/lib/python3.6/multiprocessing/process.py", line 105, in start 
[...] 
    Traceback (most recent call last): 
    BlockingIOError: [Errno 35] Resource temporarily unavailable 

Je recherchais cette erreur (BlockingIOError: [Errno 35]) et apparemment ce comportement est dû par le scrip essayant de créer plus de processus que permis par le système d'exploitation. Maintenant, je me demande: Quelle est la meilleure façon de gérer cela? Je n'imagine pas que les fonctions récursives qui utilisent le multitraitement soient une mauvaise idée en soi, n'est-ce pas? Une solution peut avoir besoin de vérifier si la limite de processus a déjà été atteinte et, si c'est le cas, de poursuivre la récursivité dans le même thread, sinon, continuez à générer de nouveaux processus, mais est-ce une bonne approche?

Répondre

1

Même lorsque le nombre d'appels récursifs est faible, votre approche est très préjudiciable pour les performances.

En règle générale, pour les types de travaux liés à la CPU, il est préférable de conserver un rapport process/processeur de 1. Pour les travaux de limites d'E/S, l'enfilage est une meilleure approche. Pour les emplois hybrides, cela devient un peu plus compliqué et seul le test de la charge de travail peut donner des réponses concrètes.

Indépendamment de la charge de travail, le développeur souhaite toujours contrôler la quantité de processus/threads que l'application va créer. Pour ce faire, le motif de conception le plus simple à utiliser est le Pool of Workers. La bibliothèque Python offre deux implémentations différentes: multiprocessing.Pool et concurrent.futures.Executor.