[Python] È stato rilasciato Python 3.5

Nicola Larosa nico a tekNico.net
Gio 17 Set 2015 16:28:50 CEST


Manlio Perillo wrote:
> Non capisco l'esempio  che fa riguardo il trasferimento del denaro, a 
> meno che mi stia perdendo qualcosa.
> 
> Glyph scrive:

...codice e testo contenenti typo sistematici: bad Glyph, bad! :-P

- "withdraw": verbo
- "withdrawal": sostantivo
- "withdrawl": nulla <https://en.wiktionary.org/wiki/withdrawl>


> "So now we have a trivially concurrent, correct version of this 
> routine, although we did have to update it a little. Regardless of 
> what sufficient_funds_for_withdrawl, deposit and withdrawl do - even 
> if they do network I/O - we know that we aren’t waiting for any of 
> them to complete, so they can’t cause transfer to interfere with
> itself."
> 
> Come sarebbe a dire che non stiamo aspettando che 
> sufficient_funds_for_withdrawl completi? La funzione controlla il
> valore restituito da quella funzione, quindi è ovvio che *dobbiamo*
> aspettare che completi.

È chiaramente un errore mettere "sufficient_funds_for_withdrawal" insieme
alle altre due.

Mi sembra anche un errore dire che non dobbiamo aspettare "deposit" e
"withdraw" se fanno I/O di rete: in quel caso sarebbero bloccanti e
dovremmo aggiungere anche a loro uno "yield from", nonostante il fatto
che non ci interessi cosa ritornano.


> Inoltre cosa intende quando dice che "se non aspettiamo che le
> funzioni completano, queste non causano la funzione transfer di
> interferire con se stessa"?
> 
> Per come leggo quell'esempio, la versione con yield equivale a 
> *serializzare* le varie operazioni che possono causare problemi se 
> eseguite in modo concorrente.

Per Glyph il paradigma cooperativo di Twisted, e di asyncio, è invisibile
come l'aria che respira, quindi non lo evidenzia. :-)

In questo modello non c'è preemption, quindi il codice può essere
interrotto solo in corrispondenza di "yield from", senza bisogno di
aggiungere lock.


> Ossia, l'esempio è equivalente a mettere un bel mu.Lock() all'inizio 
> della funzione e mu.Unlock() prima di chiamare update_balances. Per
> quel che ricordo, in Twisted quel lock è globale (come il GIL di 
> Python), quindi se quella funzione viene eseguita in una applicazione
> web, finchè il mutex è attivo *nessun* altra funzione può essere
> eseguita, anche se magari riceviamo una richiesta HTTP che non deve
> fare nessuna transazione finanziaria.

Appunto, non c'è bisogno di mettere lock: il reattore non toglie mai il
controllo al codice, se il codice non lo rilascia esplicitamente. È il
punto principale dell'intero articolo, al di là di esempi non troppo
azzeccati.


> Per farla breve: per risolvere i problemi della concorrenza
> eliminiamo del tutto la concorrenza, tranne nei punti in cui lo
> permettiamo esplicitamente.

Non la elimini in assoluto: elimini la possibilità che il controllo venga
tolto al codice in modo invisibile. Se vuoi parallelizzare azioni, ci
sono primitive per farlo esplicitamente (DeferredList in Twisted,
asyncio.wait, asyncio.as_completed, asyncio.gather in asyncio).

Da questo deriva la necessità imperativa di scrivere codice non
bloccante, e l'impossibilità di usare direttamente librerie bloccanti.


Per inciso, asyncio mi sembra parecchio più complesso di Twisted, che già
non è semplice, e prolisso in modo poco pythonico. Mah, sarà che sono
abituato a Twisted...


> La soluzione corretta è invece quella di serializzare solo la
> transazione finanziaria che coinvolge A e B, e permettere l'esecuzione
> concorrente di qualsiasi altra operazione incluse le transazioni
> finanziare che non coinvolgono A e B.

Questa è una decisione di merito che è molto facile sbagliare. Peraltro è
anche facile scrivere inavvertitamente codice bloccante in un modello
cooperativo. Non c'è pasto gratis, come al solito. :-)

-- 
Nicola 'tekNico' Larosa <http://www.tekNico.net/>


Maggiori informazioni sulla lista Python