Проблема в этой строке:
let browserContext = SynchronizationContext()
Вы вручную создали новый экземпляр SynchronizationContext
, но не связали его с потоком пользовательского интерфейса или каким-либо другим потоком. Вот почему программа аварийно завершает работу при доступе к browser.Document
, который должен быть доступен в потоке пользовательского интерфейса.
Чтобы решить эту проблему, просто используйте существующий SynchronizationContext
, который уже был связан с потоком пользовательского интерфейса:
let browserContext = SynchronizationContext.Current
Я предположил, что функция downloadWebSite
вызывается в потоке пользовательского интерфейса. Если это не так, вы можете передать контекст откуда-то в функцию или использовать глобальную переменную.
Лучший дизайн
Хотя с Async.SwitchToContext
вы можете убедиться, что следующая строка получает доступ и возвращает документ в потоке пользовательского интерфейса, но код клиента, который получает документ, может выполняться в потоке, не являющемся пользовательским интерфейсом. Лучшим вариантом является использование функции продолжения . Вместо прямого возврата документа вы можете вернуть значение SomeType
, созданное функцией продолжения, переданной в downloadWebSite
в качестве параметра. Таким образом, функция продолжения гарантированно будет запущена в потоке пользовательского интерфейса:
let downloadWebSite (address : string) cont =
let browser = new WebBrowser()
let browserContext = SynchronizationContext.Current
browser.DocumentCompleted.Add (fun _ ->
printfn "Document Loaded")
async {
do browser.Navigate(address)
let! a = Async.AwaitEvent browser.DocumentCompleted
do! Async.SwitchToContext(browserContext)
// the cont function is ensured to be run on UI thread:
return cont browser.Document }
[downloadWebSite "https://www.google.com" (fun document -> (*safely access document*))]
|> Async.Parallel
|> Async.RunSynchronously