Domanda

Ho seguito il nuovo annuncio riguardante il nuovo async funzionalità che sarà presente in C# 5.0.Ho una conoscenza di base dello stile di passaggio di continuazione e della trasformazione che il nuovo compilatore C# apporta al codice come questo frammento da Il post di Eric Lippert:

async void ArchiveDocuments(List<Url> urls)
{
  Task archive = null;
  for(int i = 0; i < urls.Count; ++i)
  {
    var document = await FetchAsync(urls[i]);
    if (archive != null)
      await archive;
    archive = ArchiveAsync(document);
  }
}

So che alcune lingue implementano le continuazioni in modo nativo, tramite call-with-current-continuation (callcc), ma non capisco davvero come funzioni o cosa faccia esattamente.

Quindi ecco la domanda:se Anders et al.aveva deciso di stringere i denti e semplicemente di implementare callcc in C# 5.0 invece di async/await caso speciale, come sarebbe lo snippet sopra?

È stato utile?

Soluzione

Risposta originale:

La tua domanda, a quanto ho capito, è "e se invece di implementare" attendere "specificamente per l'asincronia basata su attività, fosse stata implementata piuttosto l'operazione di flusso di controllo più generale di chiamata con continuazione corrente?"

Bene, prima di tutto pensiamo a cosa significa "attendere"."await" accetta un'espressione di tipo Task<T>, ottiene un waiter e chiama l'attender con la continuazione corrente:

await FooAsync()

diventa efficace

var task = FooAsync();
var awaiter = task.GetAwaiter();
awaiter.BeginAwait(somehow get the current continuation);

Supponiamo ora di avere un operatore callcc che prende come argomento un metodo e chiama il metodo con la continuazione corrente.Sarebbe simile a questo:

var task = FooAsync();
var awaiter = task.GetAwaiter();
callcc awaiter.BeginAwait;

In altre parole:

await FooAsync()

non è altro che

callcc FooAsync().GetAwaiter().BeginAwait;

Questo risponde alla tua domanda?


Aggiornamento n. 1:

Come sottolinea un commentatore, la risposta di seguito presuppone il modello di generazione del codice dalla versione "Anteprima tecnologia" della funzione async/await.Tuttavia, in realtà generiamo un codice leggermente diverso nella versione beta della funzionalità logicamente è lo stesso.Il codegen attuale è qualcosa del tipo:

var task = FooAsync();
var awaiter = task.GetAwaiter();
if (!awaiter.IsCompleted)
{
    awaiter.OnCompleted(somehow get the current continuation);
    // control now returns to the caller; when the task is complete control resumes...
}
// ... here:
result = awaiter.GetResult();
// And now the task builder for the current method is updated with the result.

Nota che questo è un po' più complicato e gestisce il caso in cui stai "aspettando" un risultato che è già stato calcolato.Non c'è bisogno di passare attraverso tutta la trafila di cedere il controllo al chiamante e riprendere da dove avevi interrotto se il risultato che stai aspettando è in effetti già memorizzato nella cache proprio lì.

Pertanto la connessione tra "await" e "callcc" non è così semplice come nella versione di anteprima, ma è comunque chiaro che stiamo essenzialmente eseguendo una callcc sul metodo "OnCompleted" dell'awaiter.Semplicemente non facciamo la chiamata se non è necessario.


Aggiornamento n. 2:

Come questa risposta

https://stackoverflow.com/a/9826822/88656

da Timwi sottolinea che la semantica di call/cc e wait non è proprio la stessa cosa;una "vera" chiamata/cc richiede che noi "catturamo" il file intero continuazione di un metodo compreso l'intero stack di chiamate, o equivalentemente che l'intero programma venga riscritto in uno stile di passaggio di continuazione.

La funzione "attesa" è più simile a una "chiamata cooperativa/cc";la continuazione coglie solo "qual è l'attuale". metodo di restituzione delle attività cosa fare dopo al momento dell'attesa?" Se il chiamante del metodo di restituzione dell'attività farà qualcosa di interessante una volta completata l'attività, quindi l'iscrizione è gratuita la sua continuazione come continuazione del compito.

Altri suggerimenti

Non sono un esperto in continuazioni, ma mi prendo una pugnalata a spiegare la differenza tra async / await e call / cc. Naturalmente questa spiegazione presuppone capisco call / cc e asincrone / attendono, che io non sono sicuro che faccio. Tuttavia, qui va ...

Con C # 'async', si indica al compilatore di generare una versione speciale di tale metodo specifico che sa come imbottigliare suo stato in un mucchio-data-struttura, in modo che possa essere "rimossa dalla pila reale" e ripreso più tardi. Da un async-contesto, "await" è quindi come "chiamata / cc" in quanto utilizza gli oggetti generati dal compilatore per imbottigliare lo stato e scendere dal "reale stack" fino al completamento dell'attività. Tuttavia, perché è il compilatore riscrittura di un metodo asincrono che permette allo stato di essere imbottigliato, attendono può essere utilizzato solo in un contesto asincrona.

In prima classe call / cc, language runtime genera tutto il codice in modo tale che la continuazione corrente può essere imbottigliato in una chiamata di continuazione funzione (rendendo la parola chiave async inutile). chiamata / cc agisce ancora come attendono, provocando lo stato attuale di continuazione (si pensi dello stato stack) da botled e passato come una funzione per la chiamata funzione. Un modo per farlo è quello di utilizzare heap-frame per tutti funzione di invocazione, invece di fotogrammi 'impilare'. (A volte indicato come 'stackless', come in 'stackless python' o molte implementazioni schema) Un altro modo è quello di rimuovere tutti i dati dal "stack reale" e roba del genere in un mucchio di dati-struttura prima di chiamare il target call / cc .

Alcune questioni spinose può sorgere se ci sono chiamate a funzioni esterne (si pensi DllImport) mescolati in pila. Ho il sospetto che questa è la ragione sono andati con l'async / implementazione attendono.

http://www.madore.org/~david/computers/callcc. html


Perché in C #, una funzione deve essere contrassegnato come "async" al fine di utilizzare questi meccanismi, mi chiedo se questo asincrona parola chiave diventerà un virus che si diffonde ad un sacco di funzioni in un sacco di librerie. Se questo accade. essi possono eventualmente realizzare essi devono applicare prima classe call / cc a livello VM, invece di questo compilatore-riscrittura basato asincrono modello. Solo il tempo lo dirà. Tuttavia, è certamente uno strumento utile nel contesto del contesto attuale C #.

I kinda inutile direi. interpreti Scheme spesso implementano call / cc con una macchina a stati che cattura stato locale sul mucchio. Quale è esattamente quello che C # 5.0 (o più correttamente C # iteratore 2.0) fa pure. Essi ha implementare call / cc, l'astrazione che è venuto in mente è molto elegante.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top