簡體   English   中英

為什么我必須包裝 Async<T> 進入另一個異步工作流程,讓! 它?

[英]Why do I have to wrap an Async<T> into another async workflow and let! it?

我試圖理解 F# 中的異步工作流,但我發現了一個我真的不理解的部分。

以下代碼工作正常:

let asynWorkflow = async{
    let! result = Stream.TryOpenAsync(partition) |> Async.AwaitTask 
    return result
    } 

let stream = Async.RunSynchronously asynWorkflow
             |> fun openResult -> if openResult.Found then openResult.Stream else Stream(partition)

我定義了一個異步工作流,其中 TryOpenAsync 返回Task<StreamOpenResult>類型。 我使用 Async.AwaitTask 將其轉換為Async<StreamOpenResult> (支線任務:“Await”Task?它不等待它只是轉換它,是嗎?我認為它與 Task.Wait 或 await 關鍵字無關)。 我用let! “等待”它let! 並將其歸還。 要啟動工作流,我使用 RunSynchronously 它應該啟動工作流並返回結果(綁定它)。 在結果上,我檢查是否找到了流。

但現在我的第一個問題。 為什么我必須將 TryOpenAsync 調用包裝在另一個異步計算中,然后讓! (“等待”)嗎? 例如以下代碼不起作用:

let asynWorkflow =  Stream.TryOpenAsync(partition) |> Async.AwaitTask  

let stream = Async.RunSynchronously asynWorkflow
             |> fun openResult -> if openResult.Found then openResult.Stream else Stream(partition)

我認為 AwaitTask 使它成為Async<T>並且RunSynchronously應該啟動它。 然后使用結果。 我想念什么?

我的第二個問題是為什么有任何“Async.Let!” 功能可用? 也許是因為它不起作用或更好為什么它不適用於以下代碼?

let ``let!`` task = async{
    let! result = task |> Async.AwaitTask 
   return result
   } 

let stream = Async.RunSynchronously ( ``let!`` (Stream.TryOpenAsync(partition))  )
         |> fun openResult -> if openResult.Found then openResult.Stream else Stream(partition)

我只是將 TryOpenAsync 作為參數插入,但它不起作用。 說不起作用我的意思是整個 FSI 將掛起。 所以它與我的異步/“等待”有關。

--- 更新:

FSI 中工作代碼的結果:

>

Real: 00:00:00.051, CPU: 00:00:00.031, GC gen0: 0, gen1: 0, gen2: 0
val asynWorkflow : Async<StreamOpenResult>
val stream : Stream

FSI 中代碼無效的結果:

>

並且您無法再在 FSI 中執行任何操作

--- 更新 2

我正在使用 Streamstone。 這里是 C# 示例: https : //github.com/yevhen/Streamstone/blob/master/Source/Example/Scenarios/S04_Write_to_stream.cs

這里是 Stream.TryOpenAsync: https : //github.com/yevhen/Streamstone/blob/master/Source/Streamstone/Stream.Api.cs#L192

如果不知道Streampartition是什么以及它們是如何工作的,我無法告訴您為什么第二個示例不起作用。

但是,我想借此機會指出,這兩個示例並不嚴格等效

F# async有點像一個“食譜”。 當您編寫async { ... } ,結果計算只是坐在那里,實際上並沒有做任何事情。 它更像是聲明一個函數而不是發出一個命令。 只有當您通過調用Async.RunSynchronouslyAsync.Start類的東西“啟動”它時,它才會真正運行。 一個推論是,您可以多次啟動同一個異步工作流,並且每次都將是一個新的工作流。 IEnumerable工作方式非常相似。

另一方面,C# Task更像是對已經在運行的異步計算的“引用”。 一旦您調用Stream.TryOpenAsync(partition) ,計算就會開始,並且不可能在任務實際開始之前獲取Task實例。 您可以多次await生成的Task ,但每次await都不會導致重新嘗試打開流。 只有第一個await才會真正等待任務完成,隨后的每個await只會返回相同的記住結果。

在 async/reactive 行話中,F# async被稱為“冷”,而 C# Task被稱為“熱”。

第二個代碼塊看起來應該對我有用。 如果我為StreamStreamOpenResult提供虛擬實現,它確實會運行它。

您應該盡可能避免使用Async.RunSynchronously因為它違背了異步的目的。 將所有這些代碼放在一個更大的async塊中,然后您就可以訪問StreamOpenResult

async {
    let! openResult = Stream.TryOpenAsync(partition) |> Async.AwaitTask  
    let stream = if openResult.Found then openResult.Stream else Stream(partition)
    return () // now do something with the stream
    }

您可能需要將Async.StartAsync.RunSynchronously放在程序的最外邊緣才能實際運行它,但最好是有async (或將其轉換為Task )並將其傳遞給其他一些代碼(例如一個網絡框架)可以以非阻塞方式調用它。

不是我想用另一個問題來回答你的問題,而是:你為什么要寫這樣的代碼? 這可能有助於理解它。 為什么不只是:

let asyncWorkflow = async {
    let! result = Stream.TryOpenAsync(partition) |> Async.AwaitTask 
    if result.Found then return openResult.Stream else return Stream(partition) }

創建異步工作流只是為了立即對其調用RunSynchronously什么意義——它類似於在Task上調用.Result它只是阻塞當前線程,直到工作流返回。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM