在进行并行计算时,为什么打印到控制台会混淆?

Sol*_*lma 2 parallel-processing console f#

运行一些执行并行计算的代码时,输​​出变得混乱:不同的消息混淆了.这是一个示例:

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
Run Code Online (Sandbox Code Playgroud)

当顺序运行相同的程序时,控制台输出很好,没有乱码.

通过此功能打印到控制台:

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 )
Run Code Online (Sandbox Code Playgroud)

并行化是由程序的这一部分完成的:

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
Run Code Online (Sandbox Code Playgroud)

有没有办法避免这种行为,例如将消息刷新到控制台?

rmu*_*unn 6

并行计算在不同的线程上运行,如果一个线程在a中间被中断printfn而第二个线程printfn在第一个线程再次运行之前运行,则它们的输出将被交错.

处理此问题的最简单方法是创建一个新函数,该函数将使用lock围绕printfn调用的关键字:

let lockObj = new obj()
let lockedPrintfn msg = lock lockObj (fun _ -> printfn msg)
Run Code Online (Sandbox Code Playgroud)

然后替换你的所有printfn通话,lockedPrintfn你应该得到你期望的序列化输出.你的性能将受到一点点,因为你的线程会偶尔会花一些时间等待printfn锁,但只要不是花的时间打印输出的计算显著需要更长的时间,你不应该真正注意到稍微慢的性能.

  • 请注意,锁定对象*需要在`lockedPrintfn`函数之外创建*.如果在函数内部创建锁定对象,则每次都会锁定*不同的*对象,这是毫无意义的,实际上根本不是锁定. (3认同)
  • 仍然很难理解你; 也许这只是你独特的[写作]风格让我失望.:-)无论如何,这个问题来自某人(根据他提出的其他问题)似乎是一个相对初学者,所以我认为对并发和真正并行编程模型之间差异的深入讨论也许应该在某个地方举行否则,因为它可能对OP无益. (3认同)
  • ... 什么?是的,.Net中的线程模型是并发的,而不是使用真正的并行性.但除此之外,我对你刚刚说过的内容只有**模糊的概念,@ user3666197.我觉得你压得太多了,我一路上都迷路了.您是否介意在Gist或其他内容中重写您的评论,其中您不限于600个字符,然后发布指向该Gist的链接? (2认同)