2011-09-15 15 views
2

私はこの小さなウェブリスナーのシミュレーションを書いた:F#の非同期廃棄

Agent.Start(fun (_ : MailboxProcessor<unit>) -> 
     let listener = new HttpListener() 
     listener.Prefixes.Add(addr) 
     listener.Start() 

     let rec respondOut() = async { 
       let! context = Async.FromBeginEnd(listener.BeginGetContext, listener.EndGetContext) 
       use s = context.Response.OutputStream 
       let wr = new StreamWriter(s) 
       use disp = { new IDisposable with 
           member x.Dispose() = 
            printfn "Disposing..." 
            wr.Dispose() } 
       wr.Write("Test") 
       return! respondOut() 
      } 

     respondOut() 
    ) 

処分は、すべてのループ上DISPで呼び出されていない理由を私は理解していませんか?

Webサービスのテキストに応答する適切な動作が何であるかをテストしたいので、私はこれをすべてやっています。

use s = Context.Response.OutputStream 
use sw = new StreamWriter(s) 
    sw.Write("test") 

または

Context.Response.Write("Test") 
Context.Response.End() 

やその他もろもろ:私はやるべきかどうかはわかりません。

ありがとうございます!

答えて

5

疑問がある場合は、反射体:)を使用してください。 useキーワードは、ブロックの最後まで "使用"のスコープを作成します。非同期ワークフロー内で使用する場合、あなたはあなたのような何かを得るだろう非同期キーワード砂糖解除場合:

Async.Bind(Async.FromBeginEnd(listener.BeginGetContext, listener.EndGetContext) 
      (fun context -> 
       use s = context.Response.OutputStream 
       let wr = new StreamWriter(s) 
       use disp = { new IDisposable with 
           member x.Dispose() = 
            printfn "Disposing..." 
            wr.Dispose() } 
       wr.Write("Test") 
       Async.ReturnFrom (respondOut()) 
       ) 

今、あなたは、「C#ので使用を置き換える場合は、再帰的関数を呼び出す継続します最後のコールAsync.ReturnFrom }ブラケットがAsync.ReturnFrom後にある場合、{} "、その後処分がブロックを行うにおける使用の一部をラッピング

と呼ばれることはありません飽きないだろう()を使用して問題を解決する必要があります:

let rec respondOut() = async { 
       let! context = Async.FromBeginEnd(listener.BeginGetContext, listener.EndGetContext) 
       do 
        use s = context.Response.OutputStream 
        let wr = new StreamWriter(s) 
        use disp = { new IDisposable with 
            member x.Dispose() = 
             printfn "Disposing..." 
             wr.Dispose() } 
        wr.Write("Test") 
       return! respondOut() 
      } 
0

私の推測では、dispは使用されていないため、コンパイルされたコードで最適化されています。次の行にprintfn "%A" dispを追加してみてください。

+0

それを削除し、パッティングwrでの使用はそれを処分しないので、それが私がリスナーとのトラブル。 –

+0

私は困惑していますが、代わりに 'using'を試しましたか? – Daniel

+0

はいうまく動作しますが、それは匿名関数の最後に配置されているためです。私の現在の問題を解決しますが、私は間違いなく使用バインディングが非同期ワークフローに配置されないことを心配しています。再帰呼び出しがテールがメモリリークの一種であると言及していたものにつながる尾最適化ではないため、おそらくそれが考えられます。 –

2

useは、ブロックの端まで延びているので、私はDisposeが(それは無条件にループするので、この場合には、決して)後再帰的演算戻ると呼ばれることが予想されます。リソースを先に処分したい場合は、何らかの形でuseの範囲を限定する必要があります。おそらくこのようなものが動作します(私は試していません):

let rec respondOut() = async { 
    let! context = Async.FromBeginEnd(listener.BeginGetContext, listener.EndGetContext) 
    do! async { 
     use s = context.Response.OutputStream 
     let wr = new StreamWriter(s) 
     use disp = { new IDisposable with 
        member x.Dispose() = 
         printfn "Disposing..." 
         wr.Dispose() } 
     wr.Write("Test") 
    } 
    return! respondOut() 
} 
+0

私は内部の 'async'が必要ではないと思います。 'use'ブロックを括弧で囲んで処分ポイントを明示的にするだけです:'(dispを使う(* dispと*何かをdisp *)) '。 – Daniel

+0

@Daniel - 計算式内で任意の式を使うことはできませんが、do!の代わりに 'do(use ...)'を使うことができます。 async {use ...} '。 – kvb

+0

おっと、忘れました。 – Daniel