Need help regarding Async and fsi

后端 未结 5 959
我寻月下人不归
我寻月下人不归 2020-12-17 05:24

I\'d like to write some code that runs a sequence of F# scripts (.fsx). The thing is that I could have literally hundreds of scripts and if I do that:

let sh         


        
5条回答
  •  南笙
    南笙 (楼主)
    2020-12-17 05:32

    Your approach looks great to me, I really like the idea of embedding process execution into asynchronous workflows using AwaitEvent!

    The likely reason why it didn't work is that you need to set EnableRisingEvents property of the Process to true if you want it to ever trigger the Exited event (don't ask my why you have to do that, it sounds pretty silly to me!) Anyway, I did a couple of other changes to your code when testing it, so here is a version that worked for me:

    open System
    open System.Diagnostics
    
    let shellExecute program args = 
      // Configure process to redirect output (so that we can read it)
      let startInfo = 
        new ProcessStartInfo
          (FileName = program, Arguments = args, UseShellExecute = false,
           WindowStyle = ProcessWindowStyle.Hidden, 
           RedirectStandardOutput = true)
    
      // Start the process
      // Note: We must enable rising events explicitly here!
      Process.Start(startInfo, EnableRaisingEvents = true)
    

    Most importantly, the code now sets EnableRaisingEvents to true. I also changed the code to use a syntax where you specify properties of an object when constructing it (to make the code a bit more succinct) and I changed a few properties, so that I can read the output (RedirectStandardOutput).

    Now, we can use the AwaitEvent method to wait until a process completes. I'll assume that fsi contains the path to fsi.exe and that scripts is a list of FSX scripts. If you want to run them sequentially, you could use a loop implemented using recursion:

    let rec loop scripts = async { 
      match scripts with 
      | [] -> printf "FINISHED"
      | script::scripts ->
        // Start the proces in background
        let p = shellExecute fsi script 
        // Wait until the process completes
        let! exit = Async.AwaitEvent p.Exited 
        // Read the output produced by the process, the exit code
        // is available in the `ExitCode` property of `Process`
        let output = p.StandardOutput.ReadToEnd()
        printfn "\nPROCESSED: %s, CODE: %d\n%A" script p.ExitCode output
        // Process the rest of the scripts
        return! loop scripts  } 
    
    // This starts the workflow on background thread, so that we can
    // do other things in the meantime. You need to add `ReadLine`, so that
    // the console application doesn't quit immedeiately
    loop scripts |> Async.Start
    Console.ReadLine() |> ignore    
    

    Of course, you could also run the processes in parallel (or for example run 2 groups of them in parallel etc.) To do that you would use Async.Parallel (in the usual way).

    Anyway, this is a really nice example of using asynchronous workflows in an area where I haven't seen them used so far. Very interesting :-)

提交回复
热议问题