Как мое ограниченное (или даже неправильное) понимание, как Async.StartImmediate, так и Async.RunSynchronously запускают асинхронное вычисление на текущем потоке. Тогда в чем же разница между этими двумя функциями? Может кто-нибудь помочь объяснить?
Update:
Изучив исходный код F # на https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/control.fs, я думаю, что я понимаю, что происходит. Async.StartImmediate запускает асинхронный поток текущего потока. После того, как он ударит асинхронную привязку, будет ли она продолжать работать в текущем потоке, зависит от самой привязки async. Например, если асинхронная привязка вызывает Async.SwitchToThreadPool, она будет работать на ThreadPool вместо текущего потока. В этом случае вам нужно будет вызвать Async.SwitchToContext, если вы хотите вернуться к текущему потоку. В противном случае, если привязка async не делает никакого переключения на другие потоки, Async.StartImmediate будет продолжать выполнять асинхронную привязку к текущему потоку. В этом случае нет необходимости вызывать Async.SwitchToContext, если вы просто хотите остаться в текущем потоке.
Причина, по которой пример Dax Fohls работает в потоке графического интерфейса, заключается в том, что Async.Sleep тщательно захватывает SynchronizationContext.Current и убедитесь, что продолжение выполняется в захваченном контексте, используя SynchronizationContext.Post(). См. https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/control.fs#L1631, где unprotectedPrimitiveWithResync обертка изменяет "args.cont" (продолжение) для публикации в захваченный контекст (см.: https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/control.fs#L1008 - trampolineHolder.Post - это в основном SynchronizationContext.Post). Это будет работать только когда SynchronizationContext.Current не является нулевым, что всегда имеет место для потока GUI. Особенно, если вы запустите консольное приложение с StartImmediate, вы обнаружите, что Async.Sleep действительно отправится в ThreadPool, потому что основной поток в консольном приложении не имеет SynchronizationContext.Current.
Итак, чтобы подвести итог, это действительно работает с потоком GUI, потому что некоторые функции, такие как Async.Sleep, Async.AwaitWaitHandle и т.д., тщательно захватывают и не забудьте вернуться к предыдущему контексту. Похоже, это преднамеренное поведение, однако это, похоже, не документировано нигде в MSDN.