2017-08-15 61 views
2

運行某些執行並行計算的代碼時,輸​​出會變成亂碼:不同的消息會混淆在一起。下面是一個示例:爲什麼在進行並行計算時打印到控制檯會混淆?

Iteration 1 
Iteration 
Iteration 23 of 19 - Calculating P&L for test window ending at 10/28/1968 12:00:00 AM 

of 
Iteration 4 
Iteration of 
Iteration 5 
Iteration 
Iteration 19 - Calculating P&L for test window ending at of 19 - Calculating P&L for test window ending at 5/29/1974 12:00:00 AM 
6 of 878/18/1971 12:00:00 AM19 - Calculating P&L for test window ending at 3/4/1977 12:00:00 AM 


of 19 of 
of 19 - Calculating P&L for test window ending at 6/25/1985 12:00:00 AM 

當運行程序順序相同的控制檯輸出出來漂亮,沒有錯亂。

let windowTrainTest (comm: Communication) critFoo count (model: IModel) (assets: Assets) (paramList: Parameters list) = 
    // Deleted some code here 
    if comm = Verbose then 
     let msg1 = sprintf "\nwindowTrainTestPandL: First date: %A, Last date: %A\nBest Criterion: %.2f\n" fDate lDate bestCriterion 
     let msg2 = sprintf "Best Parameters: %A\n" bestParameters 
     printfn "%s" <| msg1 + msg2 

    (pandl, wgts), bestParameters, (["Criterion", bestCriterion]   |> Map.ofList, 
            ["FirstDate", fDate; "LastDate", lDate] |> Map.ofList) 

並行是由程序的這一部分進行:

打印到控制檯通過這個函數來完成

let pSeqMapi f (xs: seq<'T>) = xs |> PSeq.mapi f 

let trainTest n i (trainSize, fullSize) = 
     let takenAssets = assets |> Assets.take (min fullSize len) 
     lastDate takenAssets 
     |> printfn "\nIteration %d of %d - Calculating P&L for test window ending at %A\n" (i + 1) n 
     paramList 
     |> windowTrainTest comm' critFoo trainSize model takenAssets 

    let mapTrainTest (initSizes: (int * int) list) = 
     let f = trainTest initSizes.Length 
     match calcType with 
     | PSeq -> initSizes |> pSeqMapi f |> List.ofSeq 
     | _ -> initSizes |> Seq.mapi f |> List.ofSeq 

是否有辦法避免這種行爲,對例如通過刷新消息到控制檯?

回答

1

我想我找到了解決方案,它不需要鎖。我取代了線

lastDate takenAssets 
|> printfn "\nIteration %d of %d - Calculating P&L for test window ending at %A\n" (i + 1) n 

let msg = sprintf "\nIteration %d of %d - Calculating P&L for test window ending at %A\n" (i + 1) n (lastDate takenAssets) 
printfn "%s" msg 

我留給那些更瞭解作出解釋。

+2

我認爲這是因爲'printfn'%s「msg'與C#中的'Console.WriteLine(msg)'相當。雖然'printfn「%d的\ nIteration%d - 計算盈虧在結束%A \ n個測試窗口」(我+ 1)N'會像multple調用'Console.Write' 寫入控制檯是線程安全的,因此我認爲結果。 – xuanduc987

6

並行計算在不同線程上運行,並且如果一個線程在printfn的中間中斷和第二線程中運行的printfn第一線程被再次運行之前,那麼它們的輸出將被交錯。

處理這個最簡單的方法是創建一個將使用lock關鍵字周圍printfn調用一個新的功能:

let lockObj = new obj() 
let lockedPrintfn msg = lock lockObj (fun _ -> printfn msg) 

然後用lockedPrintfn替換所有printfn電話,你應該得到的序列化輸出你我期待着。由於線程偶爾會花費一些時間來等待printfn鎖定,所以性能會受到一些影響,但只要計算花費的時間遠遠超過打印輸出的時間,則實際上應該不會注意到性能略微下降。

+3

請注意鎖對象*需要*在'lockedPrintfn'函數之外創建。如果你在函數內部創建了鎖對象,那麼你最終每次都會鎖定一個*不同的對象,這是毫無意義的,實際上根本不是鎖。 – rmunn

+1

請注意,**如果** *(引用)「.. ..如果一個線程在..中間被中斷,並且第二個線程運行..在第一個線程再次運行之前,那麼..」*這樣代碼是**不**運行trully [並行]模式的計算,但受到**只是 - [併發] **調度,取決於許多外部併發因素(避免[並行] - 調度理論)。 (供應商優化 - ) - 超標量流水線處理器體系結構針對不同的目標,而不是[並行] - 計算並且在[併發] CPU – user3666197

+2

中確實很難繞過硬連線技巧...什麼?是的,.Net中的線程模型是併發的,而不是使用真正的並行。但除此之外,我只對你剛纔所說的@ user3666197有**的想法。我想你壓縮得太多了,我迷路了。您是否介意將您的評論重寫爲Gist或其他內容,並且不限於600個字符,然後發佈該Gist的鏈接? – rmunn

相關問題