Che*_*vas 10 f# multithreading task-parallel-library async-await
注意:我不是一个专业的软件开发人员,但是我写了很多不使用异步的代码,所以如果这个问题真的很直接,我很抱歉.
我正在与用C#编写的库连接.有一个特殊的函数(让我们称之为'func')返回'Threading.Tasks.Task>'
我正在使用func在F#中构建一个库.我在控制台应用程序中测试了以下代码,它工作正常.
let result =
func()
|> Async.AwaitTask
|> Async.RunSynchronously
|> Array.ofSeq
Run Code Online (Sandbox Code Playgroud)
但是,当我从WinForms应用程序运行它(这最终是我想要做的)时,表单代码中的执行块永远不会返回.
所以我搞乱了代码,并尝试了以下,这是有效的.
let result =
async{
let! temp =
func()
|> Async.AwaitTask
return temp
} |> Async.RunSynchronously |> Array.ofSeq
Run Code Online (Sandbox Code Playgroud)
为什么第一个片段不起作用?为什么第二个片段有效?这个页面上有什么内容可以回答这些问题吗?如果是这样,那似乎并不明显.如果没有,你能指点我到哪儿吗?
第一个和第二个代码段之间的区别在于AwaitTask调用发生在不同的线程上.
试试这个验证:
let printThread() = printfn "%d" System.Threading.Thread.CurrentThread.ManagedThreadId
let result =
printThread()
func()
|> Async.AwaitTask
|> Async.RunSynchronously
|> Array.ofSeq
let res2 =
printThread()
async {
printThread()
let! temp = func() |> Async.AwaitTask
return temp
} |> Async.RunSynchronously |> Array.ofSeq
Run Code Online (Sandbox Code Playgroud)
当你跑步时res2,你会得到两行输出,上面有两个不同的数字.async运行内部的线程与res2自身运行的线程不同.潜入一个async让你在另一个线程.
现在,这与.NET TPL任务的实际工作方式相互作用.当你去等待任务时,你不只是在一些随机线程上得到一个回调,哦不!而是通过"当前"调度您的回调SynchronizationContext.这是一种特殊的野兽,其中总有一个"当前"的野兽(可以通过静态属性访问 - 谈论全球状态!),你可以要求它安排"在同一个环境中"的东西,其中的概念是"相同的上下文"由实现定义.
当然,WinForms有自己的实现,恰如其分WindowsFormsSynchronizationContext.当你在WinForms事件处理程序中运行,并且要求当前上下文安排某些事情时,它将使用WinForms自己的事件循环来调度 - 一个la Control.Invoke.
但是当然,既然你用你的方式阻止了事件循环的线程Async.RunSynchronously,那么任务就永远不会有机会发生.你正在等待它发生,它正在等你释放线程.阿卡"僵局".
要解决此问题,您需要在另一个线程上启动等待,以便不使用WinForms的同步上下文 - 您不小心偶然发现的解决方案.
另一种推荐的解决方案是通过以下方式明确告诉TPL不要使用"当前"上下文Task.ConfigureAwait:
let result =
func().ConfigureAwait( continueOnCapturedContext = false )
|> Async.AwaitTask
|> Async.RunSynchronously
|> Array.ofSeq
Run Code Online (Sandbox Code Playgroud)
不幸的是,这不会编译,因为Async.AwaitTask期望a Task,并ConfigureAwait返回一个ConfiguredTaskAwaitable.
| 归档时间: |
|
| 查看次数: |
235 次 |
| 最近记录: |