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/> _______________________________________________ Python mailing list Python@lists.python.it http://lists.python.it/mailman/listinfo/python