[英]Asynchronous computation in F#
令client为System.Net.Http.HttpClient的实例。 在下面的代码中
var response = await client.PostAsync(url, content);
processResponse(response);
在第一行和第二行代码之间没有线程块,因此,如果我们在UI线程中,则UI在POST往返期间保持响应。
获得相同的非阻塞行为的F#代码是什么? 是
let response = client.PostAsync(url, content) |> Async.AwaitTask |> Async.RunSynchronously
processResponse(response)
正确的代码? 我还不清楚RunSynchronously是否正在阻止当前线程。 如果是这样,我们如何获得与等待相同的非阻塞行为?
编辑
也许更多的上下文会有所帮助。
我有一个包含2个项目的Visual Studio解决方案:WPF / WinForms应用程序和该应用程序引用的F#库。 该库提供了一个名为FSLongWork()的函数/方法,该函数/方法使用HttpClient.GetAsync / PostAsync执行长时间的I / O操作(例如,对远程服务器的HTTP GET或POST),并返回一个字符串。 该应用程序的前端是一个带有按钮和标签的简单窗口。 按钮单击处理程序必须:1)在F#库中调用FSLongWork()2)在Label中写入依赖于步骤1中返回的字符串的内容。当然,步骤1必须异步进行,以保持UI响应性。
可能的C#应用程序解决方案
F#库:
let FSLongWork() =
async {
do! Async.Sleep(5000);
return "F#"
} |> Async.StartAsTask
C#应用点击处理程序
private async void button1_Click(object sender, EventArgs e) {
var s = await FSLongWork();
label1.Text = s;
}
C#应用程序按钮处理程序注册
this.button1.Click += new System.EventHandler(this.button1_Click);
可能的F#应用程序解决方案
F#库:
let FSLongWork() =
async {
do! Async.Sleep(5000);
return "F#"
}
F#应用点击处理程序
let button1_Click (sender : obj) e =
async {
let! s = FSLongWork()
label1.Text <- s
}
F#应用程序按钮处理程序注册
button1.Click.Add(RoutedEventHandler(fun sender e -> button1_Click sender e |> Async.StartImmediate)
我看到的问题是两种解决方案中的F#库函数(FSLongWork)不同(|> Async.StartAsTask仅在第一种中),这在可重用性方面不好。
我们可以使用F#中的第一个实现(将let! s = FSLongWork()
更改为let! s = FSLongWork() |> Async.AwaitTask
)。
第二种实现可以在C#中使用(将var s = await FSLongWork();
更改为var s2 = await Microsoft.FSharp.Control.FSharpAsync.StartAsTask(FSLongWork(), null, null);
)。
但是对我来说有点尴尬:自然的F#实现将是第二个实现(不带Async.StartAsTask
),但这需要引用Microsoft.FSharp
并使用难看的Microsoft.FSharp.Control.FSharpAsync.StartAsTask(FSLongWork(), null, null);
在C#应用中
另一方面,第一个实现(使用Async.StartAsTask
)导致在C#中更自然的使用(只需await FSLongWork()
),但是当F#应用程序使用时意味着async-> Task->异步往返
有没有一种方法可以编写F#库,以使C#用户无需引用FSharp.Core且不影响F#函数的实现方式?
参见此处: https : //docs.microsoft.com/zh-cn/dotnet/fsharp/tutorials/asynchronous-and-concurrent-programming/async
- Async.RunSynchronously将在另一个线程上启动异步工作流并等待其结果。
- Async.Start将在另一个线程上启动异步工作流,并且不会等待其结果。
因此,在这种情况下:
async {
let! response = client.PostAsync(url, content) |> Async.AwaitTask
processResponse response
} |> Async.Start
在良好的集成方面,我希望能够做到以下几点:
F#库:
let FSLongWork() =
async {
do! Async.Sleep(5000);
return "F#"
}
C#应用点击处理程序
private async void button1_Click(object sender, EventArgs e) {
var s = await FSLongWork();
label1.Text = s;
}
换句话说,当我在C#中使用await时,我希望Async <>与Task <>之间的转换是自动/隐式的。 我认为这是可能的,而我找不到解决方法,因此提出了问题。 但是显然,这是不可能的,并且需要一些手动转换管道(例如,在我报告的可能解决方案中)。
可能对将来的功能请求很重要。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.