由于我的理解有限(甚至是错误),Async.StartImmediate和Async.RunSynchronously都会在当前线程上启动异步计算.那么这两个函数之间究竟有什么区别呢?谁能帮忙解释一下?
更新:
在https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/control.fs查看F#源代码后,我想我有点理解会发生什么.Async.StartImmediate在当前线程上启动异步.在遇到异步绑定后,它是否会继续在当前线程上运行取决于异步绑定本身.例如,如果异步绑定调用Async.SwitchToThreadPool,它将在ThreadPool而不是当前线程上运行.在这种情况下,如果要返回当前线程,则需要调用Async.SwitchToContext.否则,如果异步绑定没有切换到其他线程,Async.StartImmediate将继续在当前线程上执行异步绑定.在这种情况下,如果您只想保留当前线程,则无需调用Async.SwitchToContext.
Dax Fohl的示例在GUI线程上运行的原因是因为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不为null时才有效,GUI线程始终如此.特别是,如果您使用StartImmediate在控制台应用程序中运行,您会发现Async.Sleep确实会转到ThreadPool,因为控制台应用程序中的主线程没有SynchronizationContext.Current.
总而言之,这确实适用于GUI线程,因为Async.Sleep,Async.AwaitWaitHandle等某些功能会仔细捕获并确保回发到上一个上下文.它看起来是一种故意的行为,但是这似乎没有在MSDN中的任何地方记录.
Dax*_*ohl 10
Async.RunSynchronously等到整个计算完成.因此,只要您需要从常规代码运行异步计算并需要等待结果,就可以使用它.很简单.
Async.StartImmediate确保计算在当前上下文中运行,但不等到整个表达式完成.最常见的用途(对我来说,至少)是你想要在GUI线程上异步运行计算.例如,如果你想以1秒的间隔在GUI线程上做三件事,你可以写
async {
do! Async.Sleep 1000
doThing1()
do! Async.Sleep 1000
doThing2()
do! Async.Sleep 1000
doThing3()
} |> Async.StartImmediate
Run Code Online (Sandbox Code Playgroud)
这将确保在GUI线程中调用所有内容(假设您从 GUI线程调用它),但不会在整个3秒内阻止GUI线程.如果你RunSynchronously在那里使用它,它将在一段时间内阻止GUI线程,你的屏幕将变得没有响应.
(如果你还没有完成GUI编程,那么请注意,GUI控件的更新都必须在同一个线程中完成,这可能很难手动协调;上面的内容消除了很多痛苦).
再举一个例子,这里:
// Async.StartImmediate
async {
printfn "Running"
do! Async.Sleep 1000
printfn "Finished"
} |> Async.StartImmediate
printfn "Next"
> Running
> Next
// 1 sec later
> Finished
// Async.RunSynchronously
async {
printfn "Running"
do! Async.Sleep 1000
printfn "Finished"
} |> Async.RunSynchronously
printfn "Next"
> Running
// 1 sec later
> Finished
> Next
// Async.Start just for completion:
async {
printfn "Running"
do! Async.Sleep 1000
printfn "Finished"
} |> Async.Start
printfn "Next"
> Next
> Running // With possible race condition since they're two different threads.
// 1 sec later
> Finished
Run Code Online (Sandbox Code Playgroud)
还要注意,Async.StartImmediate不能返回一个值(因为它在继续之前没有完成运行),而RunSynchronously可以.