【问题标题】:F# Async.FromBeginEnd not catching exceptionsF# Async.FromBeginEnd 没有捕获异常
【发布时间】:2012-01-04 04:37:54
【问题描述】:

我无法找出以下代码未捕获异常的原因。这是我第一次在 F# 中使用 Async,所以我确信这很简单

open System
open Microsoft.WindowsAzure
open Microsoft.WindowsAzure.StorageClient
open System.Windows.Forms

let mutable connection = "UseDevelopmentStorage=true;DevelopmentStorageProxyUri=http://ipv4.fiddler"

CloudStorageAccount.SetConfigurationSettingPublisher(fun cName cPublisher ->
                                                      cPublisher.Invoke connection |> ignore)

let storageAccount = CloudStorageAccount.Parse connection

let createTable tableName =
        let client = storageAccount.CreateCloudTableClient()
        async{
            try
                do! Async.FromBeginEnd(tableName, client.BeginCreateTable , client.EndCreateTable)
                MessageBox.Show "Created" |>ignore
            with 
            | :? StorageClientException -> printfn "failed"; MessageBox.Show("failed to create table") |> ignore
            | _ -> printfn "Failed with unknown exception"
        } |> Async.Start

[<EntryPoint; STAThread>]
let main(args) =
    let form = new Form()
    let btn = new Button(Text = "Click")
    btn.Click.AddHandler(fun _ _ -> createTable "SomeNewTable")
    form.Controls.Add btn
    let result = form.ShowDialog()
    0

如果我运行它并且表已经被创建,它表示代码中没有处理 StorageClientException 类型的异常,特别是指向 FromBeginEnd 调用的 client.EndCreateTable 部分

【问题讨论】:

  • 有点小问题,但如果你的异常处理程序被调用,它可能不会从 UI 线程调用,所以MessageBox.Show 不是一个好主意。
  • 如果将Async.Start 替换为Async.RunSynchronously 会有所不同吗?
  • 是的,messageBox 目前只是用于测试。稍后它将被替换为回调函数。换成 RunSynchronously 不会改变任何东西:(
  • 您的目标是什么版本的 .NET?你有没有安装VS2010 SP1?

标签: asynchronous f# azure-table-storage


【解决方案1】:

这听起来像是在 VS2010 SP1 的 FSharp.Core 中修复的问题。 .NET SynchronizationContexts 改变了它们的行为(我认为在 .NET 4.0 SP1 中),我们需要在 F# 运行时中进行相应的更改,以便异步正确处理线程关联。

我认为您可以在这里获取更新的 FSharp.Core:http://www.microsoft.com/download/en/details.aspx?DisplayLang=en&id=15834

【讨论】:

  • hmmmm 不幸的是没有。我已经安装了 SP1,当我点击链接时可以选择修复,我在 Visual Studio 11 预览中看到了相同的行为:(
  • 我无法在我的 SP1 盒子或 Dev11 盒子上使用@pad 的 repro 来重现这个。这种复制对你有用吗?
【解决方案2】:

如果异常的原因是表已经存在,为什么不改用BeginCreateTableIfNotExist/EndCreateTableIfNotExist呢。

更新:

该错误与 Windows Azure 无关。我可以用一个简单的程序重现相同的行为:

open System
open System.Windows.Forms

let bufferData = Array.zeroCreate<byte> 100000000

let async1 filename =        
    async{
        try 
            use outputFile = System.IO.File.Create(filename)
            do! outputFile.AsyncWrite(bufferData)   
            MessageBox.Show("OK") |> ignore         
        with 
        | :? ArgumentException -> printfn "Failed with ArgumentException"; MessageBox.Show("Failed with ArgumentException") |> ignore         
        | _ -> printfn "Failed with unknown exception"; MessageBox.Show("Failed with unknown exception") |> ignore         
    } |> Async.Start

let main(args) =
    let form = new Form(Text = "Test Form")
    let button1 = new Button(Text = "Start")
    let button2 = new Button(Text = "Start Invalid", Top = button1.Height + 10)        
    form.Controls.AddRange [| button1; button2; |]
    button1.Click.Add(fun args -> async1 "longoutput.dat")
    // Try an invalid filename to test the error case.
    button2.Click.Add(fun args -> async1 "|invalid.dat")    
    let result = form.ShowDialog()
    0

let _ = main([||])

奇怪的是代码在 F# Interactive 中运行良好,但在 Visual Studio 中作为 Windows 应用程序进行调试时无法捕获异常(无论是调试还是发布配置)。更奇怪的是,如果在 Visual Studio 之外作为应用程序执行,它又可以正常工作了。

如果您想知道,这个程序改编自 a MSDN example,它也存在同样的问题。

更新 2:

http://cs.hubfs.net/topic/Some/0/59516 提出了类似的问题。正如@ildjarn 和@Brian 所指出的,这个错误已在VS2010 SP1 中得到修复。如果没有 VS2010 SP1,或者您可以使用 F# Interactive 测试您的代码并在 VS 之外执行应用程序而不会出现任何问题。

【讨论】:

  • 这个例子很好,但是从 FromBeginEnd 构造创建的其他操作呢,这也会使对表服务的请求数量增加一倍,这对于大容量站点来说可能是非常糟糕的事情确实
  • 为什么“如果表不存在则创建表”比“创建表并在已存在时抛出异常”的请求数增加一倍?
  • CreateTableIfNotExist 仍然可以抛出StorageClientException;这不是答案。
  • @JoelMueller 我很确定,如果您通过提琴手运行请求,您会看到一个获取请求,然后是一个创建。但不管 ildjarn 是否拥有它的权利。这是一个混合应用程序,所以我不能指望总是能够访问餐桌服务
  • 您的编辑在作为已编译的应用程序运行时对我来说是正确的——即,我收到一个消息框,上面写着“ArgumentException 失败”。您的目标是什么版本的 .NET?你有没有安装VS2010 SP1?
【解决方案3】:

感谢唐赛姆, 解决方案是关闭调试“只是我的代码”。 调试 -> 选项和设置 -> 常规 -> 取消选中“仅启用我的代码(仅限托管)

这仍然是随 Windows 8 消费者预览版一起发布的 Visual Studio 11 测试版的问题。

【讨论】:

  • 这绝对是不是的解决方案。这里唯一发生的事情是,当错误发生时,调试器没有捕捉到它!
  • 在我看来这是 vs 和 F# 的问题。正如几个人在这里所说的那样,它在没有附加调试器的情况下按预期运行。这个解决方案让我重新开始解决这个问题。这不是一个“陷入困境”的修复,或者类似于在运行时忽略问题
  • 通过禁用 Just My Code,您也禁用了 VS 捕获未处理异常的能力。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-20
  • 2013-03-24
  • 2013-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多