2017-11-16 14 views
5

私はF#の非同期ワークフローを理解しようとしていますが、実際には理解できない部分があり、誰かがそれを手伝ってくれることを願っています。なぜ非同期<T>を別の非同期ワークフローにラップして、それ?

次のコードは正常に動作します:

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タスクの種類を返す非同期ワークフローを定義します。 Async.AwaitTaskを使って非同期に変換します。 (Side quest: "Await" Task?それを変換するのを待ってはいませんか?Task.Waitやawaitキーワードとは関係ありません。私はそれを "待つ"ように!それを返す。 ワークフローを開始するには、ワークフローを開始し結果を返す(バインドする)RunSynchronouslyを使用します。結果で、ストリームが見つかったかどうかを確認します。

しかし、今私の最初の質問に。 TryOpenAsync呼び出しを別の非同期計算でラップして、なぜかする必要があります。 (「待って」)それ? など。次のコードは動作しません。

私はAwaitTaskで非同期にし、RunSynchronouslyで起動すると思っていました。その後、結果を使用します。何が欠けていますか?

私の2番目の質問は、なぜ "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で動作するコードの

結果: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に何かを実行することはできませんもう一度

---更新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

+0

あなたのコードはどのように機能しませんか?エラーは何ですか? 'async'ワークフローを作成する必要はありません。 – Lee

+0

私の最後の文で述べたように、私はFSIで起動し、実行して実行します( "ハングする")。 FSIへの返答はありません。私はちょうどFSIを再起動することができます – KCT

答えて

3

は、2番目のコードブロックが見えます。私が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.StartまたはAsync.RunSynchronouslyを配置する必要があるかもしれませんが、 async(またはそれをTaskに変換して)他のコードに渡すと良いでしょう。Webフレームワーク)を非ブロッキングの方法で呼び出すことができます。

+0

良い点。原因のそれはちょうど非常に外側の端にあるべきです。しかし、私は2番目と3番目のコードブロックが動作するはずだと思っていましたが、それを理解できないと思っていました。 – KCT

3

あなたの質問に別の質問で答えたいとは思いませんが、どうしてこのようなコードをやっていますか?それはそれを理解するのに役立つかもしれません。なぜだけでなく: - それはTask.Resultを呼び出すことに似ている - それをわずか数ブロック、現在のスレッドをワークフローに戻るまで

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

だけすぐにRunSynchronouslyを呼び出すために非同期ワークフローを作成するにはほとんどのポイントがあります。

+0

バージョン2のストリームストーンはTの代わりにというタスクを返すようになったので、変更する必要がありました。 F#でasyncを使い始めると、それについてもう少し詳しく学びたいと思う。私にとっては、リスト36.2と同じように見えます。私が変換しなければならないタスクを返すメソッドを使って、あなたの本の中にある。しかし、何かが違うのはうまくいかず、私はTaskがそれと何か関係があるかどうかを調べようとします。 – KCT

+0

2番目の質問は、あなたの答えから抜粋したコードから正確に得ました。なぜ私は結果をletと結びつけなければならないのですか?結果値を次の関数にパイプすることはできません:Steam.TryOpenAsync(partition)|> Async.AwaitTask |>(楽しい結果 - > ...)または少なくとも... Async.AwaitTask |> Async "Let!" |>(楽しい結果 - > ...) – KCT

+1

おそらくAsync.mapと呼ばれるこれを行う関数を作成して、ある非同期結果を別の非同期結果にマップすることができます。これにより、非同期{}ブロックを完全に保持する必要がなくなります。 https://gist.github.com/isaacabraham/91702cd88c5912e1a968a472de727421はこれの単純なバージョンです。 –

4

Streampartitionが何であるかわからなくても2番目の例がうまくいかない理由とその働きを教えてもらえません。

しかし、この機会に、という2つの例が厳密には等価ではないことを指摘したいと思います。

F#asyncは何のための「レシピ」のようなものです。 async { ... }と書くと、実際に何もしないで計算結果がそこに座っているだけです。コマンドを発行するよりも、関数を宣言するほうが似ています。あなたがAsync.RunSynchronouslyまたはAsync.Startのようなものを呼び出すことによって "開始"するときだけ、それは実際に実行されます。当然、同じ非同期ワークフローを複数回に開始することができ、毎回新しいワークフローになる予定です。 IEnumerableの仕組みとよく似ています。これに対して、

C#Taskは、すでに実行されている非同期計算の「参照」によく似ています。 Stream.TryOpenAsync(partition)と呼ぶとすぐに計算が開始されます。実際にタスクを開始する前にTaskインスタンスを取得することは不可能です。 awaitの結果はTaskですが、それぞれawaitでストリームを開く試みはありません。最初のawaitだけがタスクの完了を実際に待つでしょう。そして、それ以降のすべてがあなたに同じ記憶された結果を返します。

非同期/反応型のヒントでは、F#asyncは「コールド」と呼ばれ、C#Taskは「ホット」と呼ばれます。

+0

私はStreamstoneを使用していますが、F#でそれをやりたいと思います。ここでC#の例https://github.com/yevhen/Streamstone/blob/master/Source/Example/Scenarios/S04_Write_to_stream.csこのレポでは、Steam.TryOpenAsync https://github.com/yevhen/ Streamstone/blob/master/Source/Streamstone/Stream.Api.cs#L192その違いを指摘してくれてありがとう。もう一度新しいことを学びました – KCT