[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