2017-03-29 6 views
2

私はFSharp.Collections.ParallelSeqretry computationを使用してスクレーパーを書いています。複数のページからHTMLを並行して取得したいと思います。失敗したときにリクエストを再試行したいと思います。例えば計算式を含むPSeq.mapがハングアップするのはなぜですか?

open System 
open FSharp.Collections.ParallelSeq 

type RetryBuilder(max) = 
    member x.Return(a) = a    // Enable 'return' 
    member x.Delay(f) = f    // Gets wrapped body and returns it (as it is) 
             // so that the body is passed to 'Run' 
    member x.Zero() = failwith "Zero" // Support if .. then 
    member x.Run(f) =     // Gets function created by 'Delay' 
    let rec loop(n) = 
     if n = 0 then failwith "Failed" // Number of retries exceeded 
     else try f() with _ -> loop(n-1) 
    loop max 

let retry = RetryBuilder(4) 

let getHtml (url : string) = retry { 
    Console.WriteLine("Get Url") 
    return 0; 
} 

//A property/field? 
let GetHtmlForAllPages = 
    let pages = {1 .. 10} 
    let allHtml = pages |> PSeq.map(fun x -> getHtml("http://somesite.com/" + x.ToString())) |> Seq.toArray 
    allHtml 

[<EntryPoint>] 
let main argv = 
    let htmlForAllPages = GetHtmlForAllPages 
    0 // return an integer exit code 

私はGetHtmlForAllPagesmainからのコードと対話しようとハングアップするようです。コードをステップ実行すると、pagesの最初の4つの値に対してPSeq.mapが処理を開始することがわかります。その上で何が起こっている

は/完全に起動しないようにretry計算式の原因は? PSeqretryの間にいくつかの奇妙な相互作用がありますか?

コードは、私がGetHtmlForAllPages機能を作成し、それを呼び出す場合は期待通りに動作します。 GetHtmlForAllPagesはフィールドであるときに何が起こっているのが不思議ですか?あなたは、静的コンストラクタ内デッドロックしているよう

答えて

5

が見えます。シナリオはhere記載されている。のみ

  • が クラスまたは任意のインスタンスの作成前に前に実行されます一度呼び出され

    • CLRはその静的コンストラクタを確保するための内部ロックを使用します静的メンバーにアクセスします。 CLRのこの動作により

    我々は静的コンストラクタ内の任意 非同期ブロック操作を実行した場合、デッドロックの可能性機会があります。 (...)

    メインスレッドは 静的コンストラクタ内に完了するためにヘルパースレッドを待ちます。ヘルパースレッドはメソッド にアクセスしているので、最初に内部ロックを取得しようとします。内部の ロックがすでにメインスレッドによって取得されているので、デッドロックの状況は になります。

    static LINQ(またはFSharp.Collections.ParallelSeqのような他の同様のライブラリ)を静的コンストラクタで使用すると、その問題が発生します。

    残念ながら、コンパイラが生成されたクラスの静的コンストラクタは、あなたのGetHtmlForAllPages値を得るものです。 (書式設定のC#と)ILSpyから:

    namespace <StartupCode$ConsoleApplication1> 
    { 
        internal static class $Program 
        { 
         [DebuggerBrowsable(DebuggerBrowsableState.Never)] 
         internal static readonly Program.RetryBuilder [email protected]; 
    
         [DebuggerBrowsable(DebuggerBrowsableState.Never)] 
         internal static readonly int[] [email protected]; 
    
         [DebuggerBrowsable(DebuggerBrowsableState.Never), DebuggerNonUserCode, CompilerGenerated] 
         internal static int [email protected]; 
    
         static $Program() 
         { 
          [email protected] = new Program.RetryBuilder(4); 
          IEnumerable<int> pages = Operators.OperatorIntrinsics.RangeInt32(1, 1, 10); 
          ParallelQuery<int> parallelQuery = PSeqModule.map<int, int>(new [email protected](), pages); 
          ParallelQuery<int> parallelQuery2 = parallelQuery; 
          int[] allHtml = SeqModule.ToArray<int>((IEnumerable<int>)parallelQuery2); 
          [email protected] = allHtml; 
         } 
        } 
    } 
    

    と実際Programクラス:

    [CompilationMapping(SourceConstructFlags.Value)] 
    public static int[] GetHtmlForAllPages 
    { 
        get 
        { 
         return [email protected]; 
        } 
    } 
    

    デッドロックがどこから来ているのです。 を(()を追加して)関数に変更すると、その静的コンストラクタの一部ではなくなり、プログラムが正常に動作します。

  • 関連する問題