WinForms App中的F#跨线程UI异常 [英] F# cross-thread UI exception in WinForms App

查看:100
本文介绍了WinForms App中的F#跨线程UI异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在用F#开发一个简单的应用程序时遇到了问题,该应用程序仅读取请求的HTML页面的长度.

I have a problem with developing a simple application in F#, which just reads the length of the requested HTML page.

在开发UI应用程序时,似乎对于VB.NET/C#语言来说,这种错误也将类似.

Seems to be that such an error would be similar for VB.NET/C# language too, when you develop the UI application.

但是我对F#还是很陌生,并且真的不敢想像要在F#中完全解决此类问题.

But I'm rather new to F# and don't really imagine hot to fix such issue exactly in F#.

F#中的源代码:

http://pastebin.com/e6WM0Sjw

open System
open System.Net
open Microsoft.FSharp.Control.WebExtensions
open System.Windows.Forms

let form = new Form()
let text = new Label()
let button = new Button()

let urlList = [ "Microsoft.com", "http://www.microsoft.com/" 
                "MSDN", "http://msdn.microsoft.com/" 
                "Bing", "http://www.bing.com"
              ]

let fetchAsync(name, url:string) =
    async { 
        try 
            let uri = new System.Uri(url)
            let webClient = new WebClient()
            let! html = webClient.AsyncDownloadString(uri)
            text.Text <- String.Format("Read %d characters for %s", html.Length, name)
        with
            | ex -> printfn "%s" (ex.Message);
    }

let runAll() =
    urlList
    |> Seq.map fetchAsync
    |> Async.Parallel 
    |> Async.RunSynchronously
    |> ignore

form.Width  <- 400
form.Height <- 300
form.Visible <- true
form.Text <- "Test download tool"

text.Width <- 200
text.Height <- 50
text.Top <- 0
text.Left <- 0
form.Controls.Add(text)

button.Text <- "click me"
button.Top <- text.Height
button.Left <- 0
button.Click |> Event.add(fun sender -> runAll() |> ignore)
form.Controls.Add(button)

[<STAThread>]
do Application.Run(form)

最好的问候,

谢谢!

推荐答案

在更新text.Text属性之前,必须将线程上下文从Async ThreadPool切换到UI线程.有关特定于F#异步的说明,请参见 MSDN链接.

You must switch thread context to UI thread from Async ThreadPool prior to updating text.Text property. See MSDN link for the F# Async-specific explanation.

通过捕获UI上下文来修改代码段后

After modifying your snippet by capturing UI context with

let uiContext = System.Threading.SynchronizationContext()

放在let form = new Form()语句之后,并将fetchAsync定义更改为

placed right after your let form = new Form() statement and changing fetchAsync definition to

let fetchAsync(name, url:string) =
    async { 
        try 
            let uri = new System.Uri(url)
            let webClient = new WebClient()
            let! html = webClient.AsyncDownloadString(uri)
            do! Async.SwitchToContext(uiContext)
            text.Text <- text.Text + String.Format("Read {0} characters for {1}\n", html.Length, name)
        with
            | ex -> printfn "%s" (ex.Message);
    }

它可以正常工作.

更新:在与一位同事讨论了调试器的特殊性之后,他强调需要干净地操作UI上下文,下面的修改对于运行方式是不可知的:

UPDATE: After discussing the debugger idiosyncrasy with a colleague, who emphasized the need of cleanly manipulating UI context, the following modification is agnostic now to the manner of run:

open System
open System.Net
open Microsoft.FSharp.Control.WebExtensions
open System.Windows.Forms
open System.Threading

let form = new Form()
let text = new Label()
let button = new Button()

let urlList = [ "Microsoft.com", "http://www.microsoft.com/"
                "MSDN", "http://msdn.microsoft.com/"
                "Bing", "http://www.bing.com"
              ]

let fetchAsync(name, url:string, ctx) =
    async {
        try
            let uri = new System.Uri(url)
            let webClient = new WebClient()
            let! html = webClient.AsyncDownloadString(uri)
            do! Async.SwitchToContext ctx
            text.Text <- text.Text + sprintf "Read %d characters for %s\n" html.Length name
        with
            | ex -> printfn "%s" (ex.Message);
    }

let runAll() =
    let ctx = SynchronizationContext.Current
    text.Text <- String.Format("{0}\n", System.DateTime.Now)
    urlList
    |> Seq.map (fun(site, url) -> fetchAsync(site, url, ctx))
    |> Async.Parallel
    |> Async.Ignore
    |> Async.Start

form.Width  <- 400
form.Height <- 300
form.Visible <- true
form.Text <- "Test download tool"

text.Width <- 200
text.Height <- 100
text.Top <- 0
text.Left <- 0
form.Controls.Add(text)

button.Text <- "click me"
button.Top <- text.Height
button.Left <- 0
button.Click |> Event.add(fun sender -> runAll() |> ignore)
form.Controls.Add(button)

[<STAThread>]
do Application.Run(form) 

这篇关于WinForms App中的F#跨线程UI异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆