从F#调用C#异步方法会导致死锁 [英] Calling C# async method from F# results in a deadlock

查看:37
本文介绍了从F#调用C#异步方法会导致死锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一组F#脚本,它们调用了我们创建的各种库,其中许多暴露了最初用C#编写的异步方法.最近,我发现这些脚本停止工作了(我认为自从我上次使用它们已经过去了大约半年,然后又恢复了工作.)

我试图找出问题所在,并提出了以下代码来重现该问题:

首先,让我们考虑一个包含以下C#类的库:

 公共类AsyncClass{公共异步Task< string>GetStringAsync(){var uri = new Uri("https://www.google.com");var client = new HttpClient();var response =等待client.GetAsync(uri);var body =等待响应.Content.ReadAsStringAsync();返回身体}} 

接下来,让我们使用以下代码从F#(FSX脚本)中调用该库:

 让asyncClient = AsyncClass()让strval1 = asyncClient.GetStringAsync()|>Async.AwaitTask |>异步运行printfn%s" strval1令strval2 =异步{返回!asyncClient.GetStringAsync()|>异步等待任务} |>异步运行printfn%s" strval2 

获得 strval1 最终会导致死锁,而 strval2 的检索就很好了(我很确定第一种情况在几个月前就可以工作了,所以看起来可能是某种更新引起的).

这很可能是同步上下文问题,其中线程基本上是等待自身完成",但是我不明白第一次调用到底有什么问题-我看不到有什么问题./p>

StackOverflow上的类似问题:

解决方案

因此,.net Task 将立即启动,而F# async {} 则是懒惰的.因此,当您将任务包装在 async {} 内时,它变得很懒,因此具有 Async.RunSynchronously 所期望的特性.

通常,我仅在执行f#异步操作时才使用async {},如果我正在使用.net任务,则将使用

Next, let's call the library from F# (FSX script) using the following code:

let asyncClient = AsyncClass()

let strval1 = asyncClient.GetStringAsync() |> Async.AwaitTask |> Async.RunSynchronously
printfn "%s" strval1

let strval2 = 
    async {
        return! asyncClient.GetStringAsync() |> Async.AwaitTask
    } |> Async.RunSynchronously
printfn "%s" strval2

Obtaining strval1 ends up with a deadlock, whereas strval2 is retrieved just fine (I am quite sure the first scenario used to work too a couple of months ago so it looks like some sort of an update might have caused this).

This is most likely a synchronisation context issue where the thread is basically "waiting for itself to finish", but I don't understand what exactly is wrong with the first call - I can't see anything wrong with it.

Similar issues on StackOverflow:

解决方案

So a .net Task will start immediately, while F# async {} is lazy. So when you wrap a task inside an async { } it becomes lazy, and thus will have the characteristics that Async.RunSynchronously is expecting.

Generally I use async {} when I'm doing f# asynchronous operations only, and if I'm working with .net Tasks I'll use TaskBuilder.fs (available in nuget). It's more aware of Task idiosyncrasies like ConfigureAwait(continueOnCapturedContext: false).

open FSharp.Control.Tasks.V2.ContextInsensitive

task {
    let! x = asyncClient.GetStringAsync()
    //do something with x
}

这篇关于从F#调用C#异步方法会导致死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆