3 user-interface f# multithreading ui-thread winforms
我在使用F#开发一个简单的应用程序时遇到问题,它只读取所请求的HTML页面的长度.
当您开发UI应用程序时,似乎这样的错误对于VB.NET/C#语言也是类似的.

但我对F#很陌生,并且真的没想象会在F#中解决这个问题.
F#中的源代码:
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)
Run Code Online (Sandbox Code Playgroud)
最好的祝福,
谢谢!
您必须Async ThreadPool在更新text.Text属性之前将线程上下文切换到UI线程.有关F#Async特定说明,请参阅MSDN链接.
通过捕获UI上下文修改您的代码段后
let uiContext = System.Threading.SynchronizationContext()
Run Code Online (Sandbox Code Playgroud)
在您的let form = new Form()陈述之后放置并将fetchAsync定义更改为
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);
}
Run Code Online (Sandbox Code Playgroud)
它没有任何问题.
更新:在与一位强调需要干净地操纵UI上下文的同事讨论调试器特性之后,以下修改现在对于运行方式是不可知的:
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)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
658 次 |
| 最近记录: |