Cet article résume ce que vous avez appris sur le multi-processus Python.
Quand le multi-processus est-il utilisé? ⇒ Lors de la réalisation d'un traitement parallèle, il est possible de diviser le processus comme moyen de réalisation.
Les applications qui exécutent des tâches gourmandes en ressources processeur sur un processeur multicœur nécessitent actuellement l'utilisation de processus multiples pour tirer parti du processeur multicœur.
https://docs.python.org/ja/3/faq/library.html#can-t-we-get-rid-of-the-global-interpreter-lock
Avant de mentionner le code source utilisant le multi-processus, je mentionnerai comment démarrer un nouveau processus. Dans n'importe quel langage de programmation, la manière de démarrer un nouveau processus est de créer un fork d'un programme. En Python, en exécutant ʻos.fork () `, chaque processus s'exécute dans un espace d'adressage différent après que le contexte mémoire a copié les processus enfants. Ci-dessous, la source.
fork.py
import os
pid_list = []
def main():
pid_list.append(os.getpid())
child_pid = os.fork()
if child_pid == 0:
pid_list.append(os.getpid())
print()
print("Enfant: こんにちは,私はEnfantプロセスです")
print("Enfant:Le numéro PID que je connais est%s" % pid_list)
else:
pid_list.append(os.getpid())
print()
print("parent:こんにちは,私はparentプロセスです")
print("parent:Le numéro PID du processus enfant est%ré"%child_pid)
print("parent:Le numéro PID que je connais est%s"%pid_list)
if __name__ == "__main__":
main()
$python fork.py
parent:こんにちは,私はparentプロセスです
parent:Le numéro PID du processus enfant est 321
parent:Le numéro PID que je connais est[320, 320]est
Enfant: こんにちは,私はEnfantプロセスです
Enfant:Le numéro PID que je connais est[320, 321]est
Le processus initial a le même 320 PID, mais vous pouvez voir que le processus enfant a ajouté 321 et que les deux processus ne partagent pas un contexte de mémoire.
La mémoire de processus n'est pas partagée par défaut. Si vous souhaitez communiquer entre les processus, vous devez effectuer un certain travail. Pour simplifier cela, le module «multiprocessing» propose plusieurs manières de communiquer entre les processus. Les deux méthodes suivantes sont présentées ici.
multiprocessing.Pipe
multiprocessing.sharedctypes
multiprocessing.Pipe
La classe Pipe a un concept similaire aux pipes Unix et Linux.
multiprocessing.Pipe ()
renvoie une paire d'objets Connection
qui représentent les deux extrémités du tube. Dans l'exemple ci-dessous (pipesample.py), parent_conn, child_conn = Pipe ()
est applicable. La valeur par défaut «Pipe (True)» le rend bidirectionnel. Avec Pipe (False)
, le pipe est unidirectionnel, et avecconn1, conn2 = Pipe ()
, conn1
est dédié à la réception de messages et conn2
est dédié à l'envoi.
La classe Pipe envoie et reçoit également des objets sélectionnables.
URL de référence: https://docs.python.org/ja/2.7/library/multiprocessing.html#pipes-and-queues
pipesample.py
from multiprocessing import Process, Pipe
class CustomClass:
pass
def work(connection):
while True:
instance = connection.recv()
if instance:
print("Enfant:Recevoir:{}".format(instance))
else:
return
def main():
parent_conn, child_conn = Pipe()
child = Process(target=work, args=(child_conn,))
for item in (
42,
'some string',
{'one':1},
CustomClass(),
None,
):
print("parent:Envoyer:{}".format(item))
parent_conn.send(item)
child.start()
child.join()
if __name__ == "__main__":
main()
$python pipesample.py
parent:Envoyer:42
parent:Envoyer:some string
parent:Envoyer:{'one': 1}
parent:Envoyer:<__main__.CustomClass object at 0x7fc785a34ac8>
parent:Envoyer:None
Enfant:Recevoir:42
Enfant:Recevoir:some string
Enfant:Recevoir:{'one': 1}
Enfant:Recevoir:<__main__.CustomClass object at 0x7fc785268978>
Si vous passez l'instance créée par for item in (42, ..., None,):
à l'argument de parent.send ()
, le processus qui est couplé en recevant child .recv ()
L'état des données est transmis à. Vous pouvez également voir que les adresses de processus sont différentes.
multiprocessing.sharedctypes
Dans la classe multiprocessing.sharedctypes
, une mémoire partagée est créée et des types de données (type int, type double, etc.) y sont créés.
Fournit un moyen d'insérer. Le type de données suit type C. Les plus basiques sont Value (typecode_or_type, * arg, lock = True)
et ʻArray (typecode_or_type, size_or_initializer, *, lock = True) .
typecode_or_typedétermine le type d'objet renvoyé. Il s'agit d'un type ctypes ou d'un code de type à une lettre tel qu'utilisé dans le module de tableau. Comme il est difficile de décrire la liste, le dictionnaire, l'espace de noms, le verrouillage, etc., utilisez
multiprocessing.Manager` dans ce cas.
Référence: https://docs.python.org/ja/3/library/multiprocessing.html#sharing-state-between-processes
valuearray.py
from multiprocessing import Process, Value, Array
def f(n,a):
n.value = 3.141592
for i in range(len(a)):
a[i] = -a[i]
if __name__ == "__main__":
num = Value('d', 0.0)
arr = Array('i', range(10))
p = Process(target=f, args=(num, arr))
p.start()
p.join()
print(num.value)
print(arr[:])
$python valuearray.py
3.141592
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
L'utilisation de plusieurs processus au lieu de threads augmentera considérablement la surcharge. L'utilisation de la mémoire augmente, surtout si chaque processus a un contexte de mémoire indépendant. Par conséquent, lorsqu'un grand nombre de processus enfants est généré, l'effet néfaste est supérieur au traitement utilisant des threads. Pour les applications multi-processus, la création d'un pool de processus est un bon moyen de contrôler l'utilisation des ressources. L'idée de base d'un pool de processus est de préparer un processus spécifié à l'avance, puis de prendre des éléments de la file d'attente et de les traiter. Au lieu de démarrer le processus après l'arrivée de la tâche à traiter, démarrez le processus à l'avance afin que le processus démarre immédiatement après l'attribution de la tâche.
Pool
Cette classe prend en charge tous les traitements compliqués qui gèrent plusieurs processus.
Le code source suivant utilise l'API Google Map de GCP (Google Cloud Platform) pour obtenir la latitude et la longitude correspondant au nom de la ville.
En définissant POOL_SIZE = 4
, quatre processus qui fonctionnent en parallèle sont spécifiés. La classe Pool
peut également utiliser le gestionnaire de contexte.
geocoding_by_multiprocessing.py
from multiprocessing import Pool
from gmaps import Geocoding
api = Geocoding(api_key='secret')
PLACES = (
'Reykjavik','Vien','Zadar',
'Venice','Wrocow','Bolognia',
'Berlin','Dehil','New York',
'Osaka'
)
POOL_SIZE = 4
def fetch_place(place):
return api.geocode(place)[0]
def present_result(geocoded):
print("{:s}, {:6.2f}, {:6.2f}".format(
geocoded['formatted_address'],
geocoded['geometry']['location']['lat'],
geocoded['geometry']['location']['lng'],
).encode('utf-8'))
def main():
with Pool(POOL_SIZE) as pool:
results = pool.map(fetch_place, PLACES)
for result in results:
present_result(result)
if __name__ == "__main__":
main()
$ python geocoding_by_multiprocessing.py
b'Reykjav\xc3\xadk, Iceland, 64.15, -21.94'
b'3110 Glendale Blvd, Los Angeles, CA 90039, USA, 34.12, -118.26'
b'Zadar, Croatia, 44.12, 15.23'
b'Venice, Metropolitan City of Venice, Italy, 45.44, 12.32'
b'Wroc\xc5\x82aw, Poland, 51.11, 17.04'
b'Bologna, Metropolitan City of Bologna, Italy, 44.49, 11.34'
b'Berlin, Germany, 52.52, 13.40'
b'Delhi, India, 28.70, 77.10'
b'New York, NY, USA, 40.71, -74.01'
b'Osaka, Japan, 34.69, 135.50'
L'étude du traitement parallèle est difficile. (Lol)
Recommended Posts