2016-01-14 10 views
9

fili di blocco è considerata una cattiva pratica per 2 motivi principali:Quali sono i vantaggi di scalabilità del codice asincrono (non bloccante)?

  1. Fili memoria costo.
  2. I thread costano il tempo di elaborazione tramite gli switch di contesto.

Qui ci sono le mie difficoltà con queste ragioni:

  1. non bloccante, il codice asincrono dovrebbe anche costare più o meno la stessa quantità di memoria, perché lo stack deve essere salvato da qualche parte a destra prima di eseguire lui asincrona chiamata (il contesto è salvato, dopo tutto). E se i thread sono significativamente inefficienti (in termini di memoria), perché il sistema operativo/CLR non offre una versione più leggera dei thread (salvando solo il contesto del callstack e nient'altro)? Non sarebbe una soluzione molto più pulita al problema della memoria, invece di costringerci a ri-architettare i nostri programmi in modo asincrono (che è molto più complesso, più difficile da capire e mantenere)?

  2. Quando un thread viene bloccato, viene messo in attesa dal sistema operativo. Il sistema operativo non cambierà il contesto al thread dormiente. Dal momento che oltre il 95% del ciclo di vita del thread è dedicato al sonno (supponendo le app vincolate a IO qui), il successo delle prestazioni dovrebbe essere trascurabile, dal momento che le sezioni di elaborazione del thread probabilmente non saranno prevenute dal sistema operativo perché dovrebbero corri molto veloce, facendo pochissimo lavoro. Quindi, per quanto riguarda le prestazioni, non riesco a vedere un gran vantaggio per un approccio non bloccante.

Cosa mi manca qui o perché questi argomenti sono viziati?

+1

Non tutti i thread sono uguali qui. Il blocco del thread dell'interfaccia utente non funziona perché l'app non risponde. –

+0

Se * si desidera * avere un codice di blocco a thread singolo, allora * sicuramente * può *. La maggior parte delle applicazioni moderne preferisce non farlo. Considera un processo in background in un'app desktop in cui desideri ancora poter interagire con l'interfaccia utente (ad esempio fornire un indicatore di avanzamento sul processo). O un'applicazione web con un throughput elevato ma pesanti operazioni di back-end, dove legare un thread del server per attendere un'operazione riduce drasticamente il numero di utenti simultanei possibili. – David

+0

Prova ad applicare i tuoi argomenti sia ad un'applicazione desktop con un'interfaccia utente reattiva che ad un'applicazione web scalabile a molte richieste –

risposta

14

Il codice asincrono, non bloccante, dovrebbe anche costare più o meno la stessa quantità di memoria, perché il callstack dovrebbe essere salvato da qualche parte proprio prima di eseguire la chiamata asincrona (il contesto viene salvato, dopotutto).

L'intera stack di chiamate è non salvate quando si verifica un await. Perché credi che l'intero stack di chiamate debba essere salvato? Lo stack di chiamate è la reificazione della continuazione e la continuazione di l'attività attesa non è la continuazione di l'attesa. La continuazione dell'attesa è in pila.

Ora, è possibile che, quando è stato atteso ogni metodo asincrono in un determinato stack di chiamate, le informazioni equivalenti allo stack di chiamate siano state memorizzate nelle continuazioni di ciascuna attività. Ma il carico di memoria di tali continuazioni è memoria heap raccolta dati inutili, non un blocco di un milione di byte di memoria stack impegnata. La dimensione dello stato di continuazione è l'ordine n nella dimensione del numero di attività; il peso di un thread è di un milione di byte sia che lo si usi o meno.

se le discussioni sono significativamente inefficienti (memoria-saggio), motivo per cui non l'OS/CLR offrire una versione più leggera di fili

Il sistema operativo fa. Offre fibre. Certo, le fibre hanno ancora uno stack, quindi forse non è meglio. Suppongo che potresti avere una discussione con una piccola pila.

Non sarebbe una soluzione molto più pulito al problema di memoria, invece di costringere a ri-architettura nostri programmi in modo asincrono

Supponiamo fatto fili - o per quella materia , processi - molto più economico. Ciò non risolve ancora il problema della sincronizzazione dell'accesso alla memoria condivisa.

Per quello che vale, penso che sarebbe bello se i processi fossero più leggeri. Loro non sono.

Inoltre, la domanda in qualche modo si contraddice. Stai lavorando con i thread, quindi sei già disposto ad assumerti l'onere della gestione delle operazioni asincrone. Un determinato thread deve essere in grado di dire un altro thread quando ha prodotto il risultato richiesto dal primo thread. Il threading implica già l'asincronia, ma l'asincronia non implica il threading. Avere un'architettura asincrona integrata nel linguaggio, il runtime e il sistema dei tipi avvantaggia solo le persone che hanno la sfortuna di dover scrivere codice che gestisce i thread.

Poiché modo oltre il 95% del ciclo di vita del filo viene speso per dormire (supponendo applicazioni IO-bound qui), il calo di prestazioni dovrebbe essere trascurabile, poiché le sezioni di trattamento del filo sarebbe probabilmente non può essere anticipata dal sistema operativo perché dovrebbero funzionare molto velocemente, facendo pochissimo lavoro.

Perché dovresti assumere un lavoratore (thread) e pagare il loro stipendio per sederti vicino alla casella di posta (dormendo il thread) in attesa che arrivi la posta (gestendo un messaggio di I/O)? Gli interrupt IO non hanno bisogno di un thread in primo luogo. Gli interrupt IO esistono in un mondo al di sotto del livello dei thread.

Non assumere un thread per attendere IO; lasciare che il sistema operativo gestisca operazioni IO asincrone. Assegna le discussioni per fare quantità enormi di elaborazione CPU ad alta latenza e quindi assegnare un thread a ciascuna CPU che possiedi.

Ora veniamo alla tua domanda:

Quali sono i vantaggi di asincrona del codice (non bloccante)?

  • Non bloccare il thread UI
  • Rendere più facile scrivere programmi che vivono in un mondo con latenza elevata
  • un uso più efficiente delle risorse della CPU limitate

Ma lasciatemi riformulare la domanda usando un'analogia. Stai gestendo una società di consegna. Ci sono molti ordini in arrivo, molte consegne che escono e non si può dire a un cliente che non si prenderà la consegna fino a quando tutte le consegne non saranno state completate.Che è meglio:

  • noleggio cinquanta ragazzi a prendere le chiamate, raccogliere pacchetti, le consegne di pianificazione, e consegnare i pacchi, e quindi richiedono che 46 di loro essere inattivo in ogni momento o

  • noleggio quattro ragazzi e ciascuno di loro è davvero bravo in un primo momento, fa un po 'di lavoro alla volta, in modo che siano sempre pronti a rispondere alle richieste dei clienti e, in secondo luogo, è davvero bravo a tenere una lista di lavori di cui ha bisogno fare in futuro

Quest'ultimo sembra un affare migliore per me.

+1

Informazioni sulla sincronizzazione: il modello async/await non risolve neanche questo, poiché la continuazione viene eseguita su un nuovo thread dal pool di thread –

+0

@WinstonSmith: La continuazione viene eseguita su un thread di lavoro in ASP.NET e console apps, ma viene eseguito sul thread dell'interfaccia utente quando è atteso sul thread dell'interfaccia utente. Ma hai ragione, ci sono sicuramente problemi di cui preoccuparsi. L'architettura asincrona cerca di mitigare molte di queste difficoltà rappresentando "un valore che apparirà in futuro" nel sistema di tipi, piuttosto che farti sincronizzare l'accesso alla memoria condivisa. –

+3

E non c'è il supporto integrato per le fibre nel CLR. Per quanto riguarda il tuo ultimo punto, perché dovrei preoccuparmi di pagare per quel lavoratore di sedersi vicino alla cassetta della posta, se è così a buon mercato? Quando dorme, non costa soldi (CPU) e quando è sveglio, lavora così velocemente che il problema del cambio di contesto dovrebbe essere un problema. Certo, c'è il problema della memoria, ma onestamente, non è un gran problema. –

-2

Stai scherzando concetti multithreading e async qui.

Entrambe le "difficoltà" derivano dal presupposto che a ogni metodo async venga assegnato un thread specializzato su cui esegue il lavoro. Tuttavia, lo stato delle cose è piuttosto opposto: ogni volta che è necessario eseguire un'operazione async, il CLR preleva un thread inattivo (quindi già creato) dal threadpool ed esegue tale metodo sul thread selezionato.

Il concetto di base qui è che async non significa sempre creare nuovi thread, significa pianificare l'esecuzione su thread esistenti in modo che nessun thread sia inattivo.

+0

In realtà, un'operazione asincrona * non rappresenta necessariamente nemmeno il lavoro svolto da un thread *. Questo è solo * un * tipo di operazione asincrona. Molte operazioni asincrone non implicano l'uso di thread. – Servy

+0

@Servy, ** c'è sempre un thread ** - il thread principale del processo. Il fatto che molte operazioni 'async' vengano pianificate per l'esecuzione su quel thread senza usare un thread separato non significa che non ci sia thread. – RePierre

+0

** No **. Un'operazione asincrona non richiede necessariamente * qualsiasi * thread per fare il suo lavoro. L'esecuzione di operazioni associate alla CPU è solo * un * tipo di operazione. Non è necessario * alcun * thread, nemmeno il thread principale, quando, ad esempio, si invia una richiesta di rete o si effettua un ritardo per un determinato periodo di tempo. Quelle sono operazioni che * non hanno bisogno di thread *. – Servy

Problemi correlati