代码之家  ›  专栏  ›  技术社区  ›  Cameron Taggart

可取消的F Async Main,但传播了异常

  •  1
  • Cameron Taggart  · 技术社区  · 8 年前

    我在找一个像F这样的解决方案# Async.Start 但不能让它接受例外。我希望控制台应用程序在未处理的异常情况下正常终止。它是一个.NET核心2.1应用程序,我需要通过响应linux sigterm和sigint信号来处理资源。如何修改此代码以使其传播 kaboom! 例外?

    let mainAsync() =
        async {
            // blow up on purpose after a number of seconds
            let seconds = 10
            printfn "%d seconds and counting" seconds
            do! (System.TimeSpan.FromSeconds(float seconds)).TotalMilliseconds |> int |> Async.Sleep
    
            //failwithf "kaboom!"
            // Update: failwithf does reproduce the problem
            // My real app is making WCF calls that are Task based and awaiting on them
            // I don't know how to make a small test case for this
            // The client is throwing a TimeoutException if unable to connect
    
            wcfClient.RetrieveServiceContentAsync someValue |> Async.AwaitTask
        }
    
    [<EntryPoint>]
    let main argv =
        use cancelMainAsync = new System.Threading.CancellationTokenSource()
        use cancelMain = new System.Threading.AutoResetEvent(false)
        let cancel() =
            if not cancelMainAsync.IsCancellationRequested then
                cancelMainAsync.Cancel()
                cancelMain.Set() |> ignore
        System.Runtime.Loader.AssemblyLoadContext.Default.add_Unloading(fun _ -> cancel())
        System.Console.CancelKeyPress.Add (fun keyPress -> keyPress.Cancel <- true; cancel())
        Async.Start(mainAsync(), cancelMainAsync.Token)
        cancelMain.WaitOne() |> ignore
        0
    
    2 回复  |  直到 7 年前
        1
  •  3
  •   Aaron M. Eshbach    8 年前
    open System
    open System.Threading
    
    let f () = 
        async {
            printfn "Running..."
            do! Async.Sleep 10000
            failwith "Kaboom!"
        }
    
    [<EntryPoint>]
    let main argv =
        use cancellation = new CancellationTokenSource()
        Console.CancelKeyPress |> Event.add (fun _ -> cancellation.Cancel(); printfn "Cancelled")
        Async.RunSynchronously(f(), cancellationToken = cancellation.Token)
        0
    

    printfn

    enter image description here

        2
  •  3
  •   Cameron Taggart    8 年前

    Async.StartWithContinuations do! Async.SwitchToThreadPool() mainAsync CancelKeyPress

    let mainAsync() =
        async {
            do! Async.SwitchToThreadPool()
    
            use precious = { new System.IDisposable with override this.Dispose() = printfn "safe haven" }
    
            // blow up on purpose after a number of seconds
            let seconds = 10
            printfn "%d seconds and counting" seconds
            do! (System.TimeSpan.FromSeconds(float seconds)).TotalMilliseconds |> int |> Async.Sleep
            failwithf "kaboom!"
    
            // the real app is has a System.TimeoutException being thrown from a C# Task
            //wcfClient.RetrieveServiceContentAsync someValue |> Async.AwaitTask
        }
    
    [<EntryPoint>]
    let main argv =
        let mutable exitCode = 0
        use cancelMainAsync = new System.Threading.CancellationTokenSource()
        use cancelMain = new System.Threading.ManualResetEventSlim()
    
        let cancel() =
            if not cancelMainAsync.IsCancellationRequested then
                cancelMainAsync.Cancel()
                cancelMain.Set()
    
        let exceptionHandler (ex: System.Exception) =
            let ex =
                match ex with
                | :? System.AggregateException as ae ->
                    if ae.InnerExceptions.Count = 1 then ae.InnerException else ex
                | _ -> ex
            printfn "%A" ex
            exitCode <- 1
            cancel()
    
        System.Runtime.Loader.AssemblyLoadContext.Default.add_Unloading(fun _ -> cancel())
        System.Console.CancelKeyPress.Add (fun args -> args.Cancel <- true; exitCode <- 2; cancel())
        Async.StartWithContinuations(mainAsync(), (fun _ -> ()), exceptionHandler, (fun _ -> ()), cancelMainAsync.Token)
        cancelMain.Wait()
        exitCode
    

    Async.RunSchronously CancellationToken OperationCancelException

    open System
    
    let mainAsync() =
        async {
            use precious = { new System.IDisposable with override this.Dispose() = printfn "safe haven" }
            // blow up on purpose after a number of seconds
            let seconds = 10
            printfn "%d seconds and counting" seconds
            do! (System.TimeSpan.FromSeconds(float seconds)).TotalMilliseconds |> int |> Async.Sleep
            failwithf "kaboom!"
            // the real app is has a System.TimeoutException being thrown from a C# Task
            //wcfClient.RetrieveServiceContentAsync someValue |> Async.AwaitTask
            return 0
        }
    
    [<EntryPoint>]
    let main argv =
        let cancellation = new Threading.CancellationTokenSource()
        let cancel() =
            if not cancellation.IsCancellationRequested then
                cancellation.Cancel()
    
        Runtime.Loader.AssemblyLoadContext.Default.add_Unloading(fun _ -> cancel())
        Console.CancelKeyPress.Add (fun event -> event.Cancel <- true; cancel())
        Async.RunSynchronously(mainAsync(), cancellationToken = cancellation.Token)
    

    open System
    
    let mainAsync() =
        async {
            use precious = { new System.IDisposable with override this.Dispose() = printfn "safe haven" }
            // blow up on purpose after a number of seconds
            let seconds = 10
            printfn "%d seconds and counting" seconds
            do! (System.TimeSpan.FromSeconds(float seconds)).TotalMilliseconds |> int |> Async.Sleep
            failwithf "kaboom!"
            // the real app is has a System.TimeoutException being thrown from a C# Task
            //wcfClient.RetrieveServiceContentAsync someValue |> Async.AwaitTask
            return 0
        }
    
    [<EntryPoint>]
    let main argv =
        let cancellation = new Threading.CancellationTokenSource()
        let cancel() =
            if not cancellation.IsCancellationRequested then
                cancellation.Cancel()
    
        Runtime.Loader.AssemblyLoadContext.Default.add_Unloading(fun _ -> cancel())
        Console.CancelKeyPress.Add (fun event -> event.Cancel <- true; cancel())
    
        try
            Async.RunSynchronously(mainAsync(), cancellationToken = cancellation.Token)
        with
        | :? OperationCanceledException -> 2
        | ex ->
            let ex =
                match ex with
                | :? AggregateException as ae ->
                    if ae.InnerExceptions.Count = 1 then ae.InnerException else ex
                | _ -> ex
            printfn "%A" ex
            1