简体   繁体   English

F#中的异步计算

[英]Asynchronous computation in F#

Let client be an instance of System.Net.Http.HttpClient . clientSystem.Net.Http.HttpClient的实例。 In the following code 在下面的代码中

var response = await client.PostAsync(url, content);
processResponse(response);

there is no thread block between the first and the second line of code, so if we are in the UI thread, the UI remains responsive during the POST round-trip. 在第一行和第二行代码之间没有线程块,因此,如果我们在U​​I线程中,则UI在POST往返期间保持响应。

What is the F# code to obtain the same non-blocking behaviour? 获得相同的非阻塞行为的F#代码是什么? Is

let response = client.PostAsync(url, content) |> Async.AwaitTask |> Async.RunSynchronously
processResponse(response)

the correct code? 正确的代码? I haven't clear whether RunSynchronously is blocking the current thread. 我还不清楚RunSynchronously是否正在阻止当前线程。 If so, how do we obtain the same non-blocking behaviour as await? 如果是这样,我们如何获得与等待相同的非阻塞行为?

EDIT 编辑

Maybe a little more context would help. 也许更多的上下文会有所帮助。

I have a Visual Studio solution with 2 projects: a WPF/WinForms app and a F# library, referenced by the app. 我有一个包含2个项目的Visual Studio解决方案:WPF / WinForms应用程序和该应用程序引用的F#库。 The library provides a function/method, named FSLongWork(), which executes a long I/O operation, eg an HTTP GET or POST to a remote server using HttpClient.GetAsync/PostAsync, and returns a string. 该库提供了一个名为FSLongWork()的函数/方法,该函数/方法使用HttpClient.GetAsync / PostAsync执行长时间的I / O操作(例如,对远程服务器的HTTP GET或POST),并返回一个字符串。 The app front-end is a simple window with a button and a label. 该应用程序的前端是一个带有按钮和标签的简单窗口。 The button click handler must: 1) Call FSLongWork() in the F# library 2) Write in the Label a content that depends on the string returned in step 1. Of course step 1 must occur asynchronously, to preserve UI responsiveness. 按钮单击处理程序必须:1)在F#库中调用FSLongWork()2)在Label中写入依赖于步骤1中返回的字符串的内容。当然,步骤1必须异步进行,以保持UI响应性。

POSSIBLE C# app SOLUTION 可能的C#应用​​程序解决方案

F# library: F#库:

let FSLongWork() = 
    async {
        do! Async.Sleep(5000);
        return "F#"
    } |> Async.StartAsTask

C# app click handler C#应用点击处理程序

private async void button1_Click(object sender, EventArgs e) {
    var s = await FSLongWork();
    label1.Text = s;
}

C# app button handler registration C#应用程序按钮处理程序注册

this.button1.Click += new System.EventHandler(this.button1_Click);

POSSIBLE F# app SOLUTION 可能的F#应用程序解决方案

F# library: F#库:

let FSLongWork() = 
    async {
        do! Async.Sleep(5000);
        return "F#"
    }

F# app click handler F#应用点击处理程序

let button1_Click (sender : obj) e = 
    async {
        let! s = FSLongWork()
        label1.Text <- s
    }

F# app button handler registration F#应用程序按钮处理程序注册

button1.Click.Add(RoutedEventHandler(fun sender e -> button1_Click sender e |> Async.StartImmediate)

The problem I see is that the F# library function (FSLongWork) is different in the two solutions (|> Async.StartAsTask is only in the first), which is not good in term of reusability. 我看到的问题是两种解决方案中的F#库函数(FSLongWork)不同(|> Async.StartAsTask仅在第一种中),这在可重用性方面不好。

We can use the first implementation in F# (change let! s = FSLongWork() to let! s = FSLongWork() |> Async.AwaitTask ). 我们可以使用F#中的第一个实现(将let! s = FSLongWork()更改为let! s = FSLongWork() |> Async.AwaitTask )。

And the second implementation can be used in C# (change var s = await FSLongWork(); to var s2 = await Microsoft.FSharp.Control.FSharpAsync.StartAsTask(FSLongWork(), null, null); ). 第二种实现可以在C#中使用(将var s = await FSLongWork();更改为var s2 = await Microsoft.FSharp.Control.FSharpAsync.StartAsTask(FSLongWork(), null, null); )。

Yet it looks a bit awkward to me: the natural F# implementation would be the second (without Async.StartAsTask ), but this requires to reference Microsoft.FSharp and the use of the rather ugly Microsoft.FSharp.Control.FSharpAsync.StartAsTask(FSLongWork(), null, null); 但是对我来说有点尴尬:自然的F#实现将是第二个实现(不带Async.StartAsTask ),但这需要引用Microsoft.FSharp并使用难看的Microsoft.FSharp.Control.FSharpAsync.StartAsTask(FSLongWork(), null, null); in the C# app. 在C#应用中

On the other hand, the first implementation (with Async.StartAsTask ), leads to a more natural use in C# (simply await FSLongWork() ), but implies am async->Task->async round-trip when used by a F# app 另一方面,第一个实现(使用Async.StartAsTask )导致在C#中更自然的使用(只需await FSLongWork() ),但是当F#应用程序使用时意味着async-> Task->异步往返

Is there a way to write the F# library so that a C# user doesn't need to reference FSharp.Core and without influencing how the F# function is implemented? 有没有一种方法可以编写F#库,以使C#用户无需引用FSharp.Core且不影响F#函数的实现方式?

see here: https://docs.microsoft.com/en-us/dotnet/fsharp/tutorials/asynchronous-and-concurrent-programming/async 参见此处: https : //docs.microsoft.com/zh-cn/dotnet/fsharp/tutorials/asynchronous-and-concurrent-programming/async

  1. Async.RunSynchronously will start an async workflow on another thread and await its result. Async.RunSynchronously将在另一个线程上启动异步工作流并等待其结果。
  2. Async.Start will start an async workflow on another thread, and will not await its result. Async.Start将在另一个线程上启动异步工作流,并且不会等待其结果。

So in this case: 因此,在这种情况下:

async {
  let! response = client.PostAsync(url, content) |> Async.AwaitTask
  processResponse response
} |> Async.Start

In terms of good integration I would expect to be able to do as follows: 在良好的集成方面,我希望能够做到以下几点:

F# library: F#库:

let FSLongWork() = 
    async {
        do! Async.Sleep(5000);
        return "F#"
    }

C# app click handler C#应用点击处理程序

private async void button1_Click(object sender, EventArgs e) {
    var s = await FSLongWork();
    label1.Text = s;
}

In other words, I would expect that the conversion between Async<> and Task<> was automatic/implicit when I use await in C#. 换句话说,当我在C#中使用await时,我希望Async <>与Task <>之间的转换是自动/隐式的。 I thought that this was possible and that I wasn't able to find the way to do it, hence my questions. 我认为这是可能的,而我找不到解决方法,因此提出了问题。 Apparently, though, it is not possible and some manual conversion plumbing is required (as for example in the the possible solutions I reported). 但是显然,这是不可能的,并且需要一些手动转换管道(例如,在我报告的可能解决方案中)。

Maybe it could be material for a future feature request. 可能对将来的功能请求很重要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM