【问题标题】:Can't find simple way to wait asynchronously找不到简单的异步等待方法
【发布时间】:2021-10-06 16:29:01
【问题描述】:

假设我需要执行一些异步操作并且我现在需要结果。如果我使用 C#,我会这样做:

var contents = await File.ReadAllLinesAsync(...);

这里重要的是它是非阻塞操作。

我知道 Async.RunSynchronously,但文档表明它是 阻塞 操作,这意味着它不比 C# 中的 Task.Result 好。

如果我需要在 F# 中做同样的事情,我如何在不阻塞的情况下读取文件内容?

【问题讨论】:

    标签: f#


    【解决方案1】:

    正如 Bartosz 的回答中所解释的,在 F# 中的 async 方法中使用 await 相当于在计算表达式块中使用 let!。如果您正在处理.NET 任务,您可以使用task { ... };如果您正在处理 F# 异步计算,那么您可以使用 async { ... }

    要使用async { .. },您还可以使用Async.AwaitTask 将任务转换为异步计算:

    let run () = async {
      let! contents = File.ReadAllLinesAsync "/path" |> Async.AwaitTask
      return contents
    }
    

    如何实际运行run () 函数?首先,没有办法运行计算并等待结果而不阻塞。如果您需要同步代码块中的结果,则只需阻塞即可。这与任务相同 - 您可以让它们在后台运行和完成,在另一个非阻塞任务中使用它们或阻塞并等待。

    如果您不在另一个 async { .. } 块内,您可以做的两件事是:

    // Run the task, do something with the result and
    // start the whole operation without blocking 
    async { 
      let! result = run ()
      printf "%s" result }
    |> Async.Start
    
    // Block and wait for the result using RunSynchronously
    let result = 
      run ()
      |> Async.RunSynchronously
    

    【讨论】:

    • 感谢您的解释!我无法理解的是为什么我可以在 C# 中等待结果而不阻塞,但在 F# 中却不能?为什么会有如此根本的区别?
    • 我相信你也不能在 C# 中做到这一点(因为这是异步计算的本质_。你正在考虑的 C# 代码是什么?
    • 或者你的意思是使用await?如果是这样,那么您只能在标记为async 的方法中执行此操作,而这又会使该方法返回Task。因此,这与将 F# 代码包装在 async { ... } 中完全相同,这也将使其返回 Async,即延迟计算。
    • 我的意思是等待,是的。在 C# 中,我可以拥有一堆异步标记的方法并等待所有内容,而且我永远不必问自己“我是阻塞并等待结果还是写延续”。在 C# 中,我可以等待所有内容并编写代码,就好像它们都是同步的一样。我试图了解如何在 F# 中做同样的事情
    • 在 F# 中,完全一样 - 你编写代码,因为它在 async { .. } 块内都是同步的(相当于将方法标记为 async)并且在这些块内,你调用其他异步代码使用let!(相当于await)。有关更多详细信息,这可能会有所帮助:tomasp.net/blog/csharp-fsharp-async-intro.aspx
    【解决方案2】:

    如果你想坚持使用任务并行库:

    open FSharp.Control.Tasks
    
    let run () = task {
        let! contents = File.ReadAllLinesAsync "/path"
        return contents
    }
    

    这里task { .. }类似于C#中async Task<T>的标记方法,而以!符号结尾的组合符(例如let!)可以与await进行比较。

    在不深入讨论实现细节的情况下,主要区别在于task { .. } 是一个计算表达式,因此它是可插拔的——FSharp.Control.Tasks 命名空间中默认存在一个,但也有其他实现和库(例如Ply) 有时会提供不同的功能。

    【讨论】:

    • 有没有办法在不阻塞的情况下等待 F# 本机异步(非任务)?
    • 为此,您可以将代码包装在async { ... } 中并在块内使用let!,方式与上述答案完全相同。
    • @TomasPetricek 如果我有 async{...} 和 let!在里面,我如何在不阻塞的情况下实际运行 async 本身?除了 Async.RunSynchronously 之外找不到任何东西
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多