2011-12-12 4 views
6

C'est probablement l'une des choses les plus élémentaires de F #, mais je viens de réaliser que je n'ai aucune idée de ce qui se passe derrière les sceens.Attendre sans bloquer le fil? - Comment?

let testMe() = 
    async { printfn "before!" 
      do! myAsyncFunction() // Waits without blocking thread 
      printfn "after!" } 

testMe |> Async.RunSynchronously 

Que se passe-t-il à do! myAsyncFunction()? Je suis conscient du fait qu'il attend myAsyncFunction pour finir, avant de continuer. Mais comment peut-il faire cela, sans bloquer le fil?

Ma meilleure estimation est que tout après do! myAsyncFunction() est passé le long comme une continuation, qui est exécuté sur le même thread myAsyncFunction() était prévu, une fois myAsyncFunction() a terminé son exécution .. mais là encore, c'est juste une supposition.

+1

Votre supposition est juste, même si je pense que la poursuite sera exécuté sur le prochain fil disponible à partir du pool de threads, pas nécessairement le même fil. – Daniel

Répondre

5

Comme vous l'avez correctement souligné, le myAsyncFunction est passé une continuation et l'appelle pour reprendre le reste du flux de travail asynchrone lorsqu'il se termine.

Vous pouvez le comprendre mieux en regardant la version Dessucré du code:

let testMe() = 
    async.Delay(fun() -> 
    printfn "before!" 
    async.Bind(myAsyncFunction(), fun() -> 
     printfn "after!" 
     async.Zero())) 

Ils élément clé est que le flux de travail asynchrone créé par myAsyncFunction est donnée à l'opération Bind qui commence et lui donne la second argument (une continuation) en tant que fonction à appeler lorsque le workflow est terminé. Si vous simplifiez beaucoup, alors un flux de travail asynchrone pourrait être défini comme ceci:

type MyAsync<'T> = (('T -> unit) * (exn -> unit)) -> unit 

Ainsi, un flux de travail asynchrone est juste une fonction qui prend des continuations comme argument. Quand il obtient les suites, il fait quelque chose (c'est-à-dire crée une minuterie ou démarre des E/S) et ensuite il appelle finalement ces continuations. La question "Sur quel fil sont les continuations appelées?" est intéressant - dans un modèle simple, cela dépend du MyAsync que vous démarrez - il peut décider de les exécuter où bon lui semble (par exemple Async.SwithcToNewThread les exécute sur un nouveau thread). La bibliothèque F # inclut des manipulations supplémentaires qui facilitent la programmation de l'interface graphique en utilisant les workflows.

Votre exemple utilise Async.RunImmediate, qui bloque le thread en cours, mais vous pouvez également utiliser Async.Start, qui démarre le workflow et ignore le résultat lorsqu'il est généré. La mise en œuvre de Async.Start pourrait ressembler à ceci:

let Start (async:MyAsync<unit>) = async (ignore, ignore) 
Questions connexes