Reputation: 582
I call this this function from the GUI thread:
let updateImageLoop (pprocess : PlotProcess) (target : IUpdatableImageView<'T>) =
async {
while target.Continue do
let context = System.Threading.SynchronizationContext.Current
do! Async.SwitchToThreadPool()
System.Threading.Thread.Sleep(10000)
do! Async.SwitchToContext(context)
let image = target.CreateImage()
match image with
| Some userImage -> do! target.UpdateImageView userImage
| None -> ()
} |> Async.StartImmediate
The problem comes when the method target.UpdateImageView is executed, an exception is generated:
The calling thread must be STA, because many UI components require this.
I know that, but that is what i did with
do! Async.SwitchToContext(context)
Eliminating the functions SwitchToContext and SwitchToThreadPool, removes the exception, but the GUI just freezes. And that makes sense, but why i can't make the switch between threads??
The function that generates the problem is UpdateImageView. I tested it with and without making it async.
member this.UpdateImageView etoimage =
async {
let imageview = new Eto.Forms.ImageView()
imageview.Image <- etoimage
this.Content <- imageview
}
edit ---
Testing with this code:
let updateImageLoop (pprocess : PlotProcess) (target : IUpdatableImageView<'T>) =
let context = System.Threading.SynchronizationContext.Current
let printThread text =
printfn "[%d] %s" System.Threading.Thread.CurrentThread.ManagedThreadId text
async {
while target.Continue do
printThread "begining"
do! Async.SwitchToThreadPool()
printThread "after swith to thread pool"
let image = target.CreateImage()
match image with
| Some userImage ->
printThread "before switch to context"
do! Async.SwitchToContext context
printThread "after switch to context"
target.UpdateImageView userImage
| None -> ()
} |> Async.StartImmediate
Prints :
[1] begining
[4] after swith to thread pool
[4] before switch to context
[5] after switch to context
Upvotes: 5
Views: 972
Reputation: 1159
Use [< STAThread >]
Use the guiContext to work on the GUI
In your GUI creation (framework init) remember the guiContext
let guiContext = System.Threading.SynchronizationContext.Current
and pass it into async GUI execute
// the async GUI execute
async {
let currentContext = System.Threading.SynchronizationContext.Current
do! Async.SwitchToContext(guiContext)
f() // work on the GUI
do! Async.SwitchToContext(currentContext)
}
Put the waiting in an extra step to keep it composable.
Upvotes: 4