Un très simple multithreading parallèle URL extraction (sans file d'attente)
J'ai passé une journée entière à la recherche pour le plus simple possible multithread URL de l'outil de récupération en Python, mais la plupart des scripts que j'ai trouvé sont à l'aide de files d'attente ou de multitraitement ou bibliothèques complexes.
Enfin je l'ai écrit moi-même, que je suis déclaration comme une réponse. N'hésitez pas à suggérer toute amélioration.
Je suppose que d'autres personnes pourraient avoir été la recherche de quelque chose de similaire.
- juste pour ajouter:en Python cas, le multithreading, n'est pas natif de base en raison de GIL.
- Il alambics semble que l'extraction de l'Url en parallèle est plus rapide que de le faire en série. Pourquoi est-ce? est-ce dû au fait que (je suppose) l'interpréteur Python n'est pas exécuté en continu pendant une requête HTTP?
- Mais si je veux analyser le contenu de ces pages web, j'extrais? Est-il préférable de faire l'analyse dans chaque thread, ou devrais-je le faire de façon séquentielle après avoir rejoint les threads de travail pour le thread principal?
Vous devez vous connecter pour publier un commentaire.
Simplifier votre version d'origine autant que possible:
La seule nouvelles astuces sont ici:
join
déjà dit à vous que.Thread
sous-classe, juste untarget
fonction.for
boucle?append
, comme ceci. Ou, alternativement, d'écrire un wrapper qui crée, démarre, et renvoie un fil, comme ceci. De toute façon, je pense que c'est moins simple (bien que le second est un moyen utile de revoir les cas compliqués, il ne fonctionne pas quand les choses sont déjà très simple).thread.start()
retourné le fil, que vous pouvez mettre la création et de commencer à assembler en une seule expression. En C++ ou JavaScript, vous auriez probablement le faire. Le problème est que, tandis que le chaînage de méthode et d'autres "fluent de la programmation" les techniques de rendre les choses plus concis, ils peuvent aussi se décompose l'expression/déclaration de la frontière, et sont souvent ambigus. donc, Python va dans presque l'exact opposé de la direction, et près de la aucun méthodes ou des opérateurs de retour de l'objet qu'ils opèrent. Voir en.wikipedia.org/wiki/Fluent_interface.multiprocessing
a un pool de threads qui ne démarre pas à d'autres processus:Les avantages par rapport à
Thread
solution basée sur:ThreadPool
permet de limiter le nombre maximum de connexions simultanées (20
dans l'exemple de code)from urllib.request import urlopen
sur Python 3).results
, si nous appelons.join()
ou.terminate()
pour terminer les processus? Ou nous n'avons pas besoin de le faire pourThreadPool
?.terminate()
ou de l'utilisationwith
-déclaration:with Pool() as pool: ...
.Le principal exemple dans le
simultanées.les contrats à terme
est tout ce que vous voulez, beaucoup plus simplement. De Plus, il peut gérer un grand nombre de Url de ne faire que 5 à la fois, et il gère les erreurs beaucoup plus de bien.Bien sûr, ce module est construit uniquement avec Python 3.2 ou plus tard... mais si vous êtes en utilisant 2.5-3.1, vous pouvez simplement installer le backport,
terme
, hors PyPI. Tout ce que vous devez changer à partir de l'exemple de code est à rechercher-remplacerconcurrent.futures
avecfutures
, et, pour les 2.x,urllib.request
avecurllib2
.Voici l'exemple reporté à 2.x, modifié pour utiliser votre liste d'URL et ajouter du temps:
Mais vous pouvez rendre le tout encore plus simple. Vraiment, tout ce que vous avez besoin est:
Je suis désormais la publication d'une solution différente, par avoir les threads de travail non-démon et se joindre à eux pour le thread principal (ce qui signifie bloquer le thread principal jusqu'à ce que tous les threads de travail ont fini) au lieu de notifier la fin de l'exécution de chaque thread de travail avec un rappel à une fonction globale (comme je l'ai fait dans la réponse précédente), comme dans certains commentaires, il a été noté que cette voie n'est pas thread-safe.
thread is threading.currentThread()
n'est pas garanti de fonctionner (je pense que ce sera toujours le cas pour n'importe quel Disponible version à ce jour, sur n'importe quelle plateforme avec de vrais fils, si elle est utilisée dans le thread principal... mais tout de même, mieux vaut ne pas assumer). Plus sûr de stocker toutes lesThread
des objets dans une liste (threads = [FetchUrl(url) for url in urls]
), puis démarrer, puis se joindre à eux avecfor thread in threads: thread.join()
.Thread
sous-classe, sauf si vous avez une sorte d'état de stocker ou de certaines API pour interagir avec le filetage de l'extérieur, il suffit d'écrire une fonction simple, et nethreading.Thread(target=my_thread_function, args=[url])
.threading.enumerate()
inclut uniquement les threads du processus actuel, le fait d'exécuter plusieurs copies de la même script dans des instances distinctes de Python exécute en tant que processus distinct n'est pas un problème. C'est juste que si vous décidez plus tard de s'étendre sur ce code (ou de l'utiliser dans un autre projet), vous pourriez être fils de démon créé dans une autre partie du code, ou de ce qui est maintenant le code principal peut-être même code s'exécutant dans certains thread d'arrière-plan.threading.enumerate()
explication du sens pour moi! merci beaucoup pour le code pastebin.com/Z5MdeB5x, si vous le coller dans une nouvelle réponse, je vais accepter la réponse sommet!Ce script récupère le contenu à partir d'un ensemble d'Url définie dans un tableau. Il donne naissance à un thread pour chaque URL à récupérer, de sorte qu'il est destiné à être utilisé pour un ensemble limité d'Url.
Au lieu d'utiliser une file d'attente de l'objet, chaque thread est en notifiant sa fin avec un rappel à une fonction globale, qui tient compte du nombre de threads en cours d'exécution.
urlsToFetch-=1
. À l'intérieur de l'interprète, qui compile en trois étapes distinctes pour chargerurlsToFetch
, de soustraire un, et de les stockerurlsToFetch
. Si l'interprète de commutateurs fils entre la charge et de la boutique, vous vous retrouverez avec 1 thread de chargement à 2, alors le thread 2 chargement du même 2, alors le thread 2 le stockage d'un 1, alors le thread 1 stockage 1.threading.Lock
autour de chaque accès à la variable, ou beaucoup d'autres possibilités (utiliser un comptés sémaphore au lieu d'un simple entier, ou l'utilisation d'une barrière, au lieu de compter explicitement, ...), mais vous n'avez pas vraiment besoin de ce mondial à tous. Justejoin
tous les threads au lieu de daemonizing eux, et c'est fait quand vous avez rejoint tous.concurrent.futures
chaque fois que possible.thread.join
est un appel bloquant—il attend sans l'aide de n'importe quel CPU jusqu'à l'OS lui dit de se réveiller parce quethread
a fini.'"%s" fetched'
au lieu de"\"%s\" fetched"
. (Et si vous avez besoin de guillemets simples et doubles dans la même chaîne, il suffit d'utiliser """triple guillemets doubles""".)