[Python] Applicazione multithreading

Valentino Volonghi aka Dialtone dialtone a divmod.com
Lun 19 Mar 2007 12:51:22 CET


On Mon, 19 Mar 2007 12:04:20 +0100 (CET), Daniele Varrazzo <piro a develer.com> wrote:
>Come già spiegato anche da Valentino, non sempre "multithreading" implica
>"più veloce". Ma nel tuo caso è così, caschi bene :)

Beh no, dissento profondamente. Quantomeno e` veloce uguale, in python e` solo piu` lento.
Secondariamente in questo caso usi un quantitativo di ram proibitivo per fare poco o nulla e il
codice risultante e` incredibilmente piu` complesso di quelle 20 righe che ho scritto che facilmente si possono riadattare a fare il crawler.

>In fondo al messaggio attacco uno script che avevo scritto un po' di tempo
>fa: usa diversi thread per scaricare una serie di file. Non è uno spider
>perché la lista di cose da scaricare va specificata all'inizio, ma penso sia
>chiara la modifica da effettuare: quando un'istanza di Job trova una url
>"interessante", deve aggiungerla alla lista "DownloadManager.urls" dei
>compiti da svolgere. Dovresti modificare la signature di Job.__init__ in
>modo che possa ricevere un puntatore a tutto il download manager (che nome
>borioso!) anziché al solo callback che notifica dell'avvenuto download: io
>aggiungerei un metodo DownloadManager.add_job(...) che aggiunga il nuovo job
>alla lista rispettando il lock.

Cosi` a naso direi che il tuo codice contiene almeno una race condition che solo casualmente
non hai mai incontrato nella vita reale.

Infatti il loop nell'__init__ del download manager usa: url, filename = self.urls.pop()
senza pero` acquisire e rilasciare il lock su urls. Questo significa che se un download
finisce prima che possa terminare questo loop abbiamo almeno due risorse che accedono
contemporaneamente a quella variabile e chissa` cosa succede :). Sistemarlo e` banale,
tuttavia in 100 righe di codice molto semplice e con un solo lock da gestire c'e` stato
spazio per una race condition, cosi` facile non deve essere.

>Spero ti serva di "ispirazione": ci ho messo pochi minuti a scriverlo, ha
>fatto il suo porco lavoro e in effetti il fatto che in Python si scriva un
>download manager multi-thread in meno di 100 righe fa la sua figura :)

Se togliamo la race condition, forse.

E` proprio una questione architetturale: con il multi-threading l'esecuzione e` al massimo
veloce uguale e in rari casi e neanche troppo frequenti. Il multi processing e` _sempre_ piu` veloce (e raramente anche linearmente piu` veloce) nei casi in cui si puo` applicare (posto che il tempo di startup di un processo e` piu` elevato rispetto a quello di un thread in molte architetture). Entrambi occupano piu` risorse dello stretto indispensabile. Come anche Ludo potrebbe facilmente mostrare (senza usare Twisted) e` abbastanza semplice ottenere un client http asincrono estremamente veloce.

Essere asincroni permette di non sprecare inutilmente risorse, permette di essere molto piu` veloci (si possono gestire con pochissimi problemi di scalabilita` un numero esagerato di client essendo quindi piu` veloci a scaricare le pagine), permette di evitare i problemi di locking. Inoltre, ma questo
piu` che altro riguarda python, si possono sfruttare efficacemente i multi-processori avviando piu`
processi di questo crawler asincrono che lavorano su due sezioni (o in altro modo analogo) della
lista degli url di partenza per poi farsi il proprio giretto nella rete sfruttando al 100% processori
multipli. Esistono situazioni in cui il multi-threading e` utile (al di la` del kernel level) ma per tutto il resto questi risultano essere semplicemente un'astrazione sbagliata, specialmente se, come nel tuo script, sono usati con shared state invece che con message passing attraverso le Queue.


Maggiori informazioni sulla lista Python