views:

1598

answers:

2

...or...

"What evil in the depths of WPF have I awoken?"

I'm creating a Canvas on a background thread and rendering it to a bitmap. I've had this working in production code for over a year now without problems. I do the following:

  • create a Canvas object
  • create a new NameScope object
  • assign that NameScope to the Canvas
  • draw whatever I want on the Canvas
  • call canvas.Measure() with the Canvas's size
  • call canvas.Arrage() with the Canvas's available rect
  • call canvas.UpdateLayout()
  • render the Canvas

In the draw step, I have always just called canvas.Children.Add() to put UIElements onto the Canvas. This has always worked.

Now, for some inexplicable reason, in one specific case in the application I'm working on, the call to canvas.Children.Add() hangs indefinetely, blocking my background thread. I can't think of anything I'm doing differently between the code that has been working for over a year, and this one specific case.

Can anyone suggest possible reasons why a call to canvas.Children.Add() would hang like this?

Edit: The background thread is an STA thread (the background thread processing model was put in place because I couldn't process images using WPF on a MTA thread), so the thread apartment model shouldn't be the culprit.

Edit #2: While I understand why people are suggesting I try Dispatcher.BeginInvoke() from my background thread, I don't like that option for two reasons:

  1. I want my background thread processing to be synchronous on that thread. My background thread has a queue that other threads submit image jobs to, and my background thread processes each job as it gets to them. Using Dispatcher.BeginInvoke() adds another layer of complexity that I'd rather avoid.
  2. I've never needed to until now. Doing this background processing synchronously on my background thread has just plain worked. I'm trying to determine what could possibly be different about this bizarre edge case that causes this code to not work. If I can't get it to work, I'll end up rewriting this processing code without WPF, which I'd also rather avoid.
+2  A: 

What apartment model are you using for your background thread?

I believe WPF needs to run on the STA thread. When you spawn the background thread, try settings it's apartment to STA.

Update:

If the STA thread is not the problem, then I would try breaking your canvas drawing up into chunks. Basically if you do a:

Dispatcher.BeginInvoke(...)

from your thread, the provided delegate gets pushed onto the back of the dispatcher queue, allowing other queued tasks to execute.

Update 2:

You could also try debugging into the source code for the Canvas object using the .NET framework reference sources. You can enable this by turning on "enable .net framework source stepping" in the debugging options under Tools->Options.

Scott Wisniewski
It's an STA thread. That was the whole reason I had to offload my processing to a background thread - the calling thread is our case was always MTA.
unforgiven3
Wouldn't Dispatcher.BeginInvoke() defeat the purpose of WPF processing on a background thread? I don't want to shunt the task back to the UI thread..
unforgiven3
I meant try using the canvas's dispatcher.try "theCanvas.Dispatcher.BeginInvoke()".
Scott Wisniewski
I'd prefer not to go the route of Dispatcher.BeginInvoke() if I can get away without it - see my "Edit #2" in the original question...
unforgiven3
Dispatcher.BeginInvoke will not behave asynchronously if it is called from the thread that owns the dispatcher. In that case, the provided delegate just gets added to the end of the dispatcher queue. That gives the message loop a chance to execute any other tasks that are pending on the queue.
Scott Wisniewski
Please note, however, that I'm not sure if that will actually solve your problem. It's just speculation. The way to really figure it out the root cause is to debug the code using the reference sources. That way you can see exactly what is locking your thread.
Scott Wisniewski
Hmmm, interesting - my only problem with that is that I really want to wait around for the result of the processing - how can I know when it's been pulled off the dispatcher queue and executed?
unforgiven3
You'll know because your delegate gets called. The code will run on the same thread that it did before, except that it will be interleaved with high priority "UI" code resulting from your changes to the canvas.When the last delegate finishes executing you can use the same notification mechanism you were using to notify your "MTA thread" in the old scheme.But, I think it's worth while for you to debug the code before trying this to find what the real cause is.
Scott Wisniewski
Yeah, I think I'll try and see what is going on in Canvas. Thanks for your help!
unforgiven3
Did you ever find the root cause of the problem?
Scott Wisniewski
A: 

Try calling Dispatcher.Run() in the background thread.

mdm20
... can you elaborate on that? Dispatcher.Run() is generally used for executing some logic on the WPF UI thread. I explicitly want to execute my code on a background thread, *not* the UI thread.
unforgiven3
But, if you are using WPF objects on your background thread then that thread IS a UI thread.
Scott Wisniewski
@Scott, can you provide me with some MS documentation that says that? Not that I don't believe you :-) I just want to make sure I understand the implications of background processing with WPF correctly...
unforgiven3
No I can't. However the Canvas class inherits from DispatcherObject. That means it uses a dispatcher associated with the thread it's created on. In this case that means your bg thread. Take a look at the docs for DispatcherObject
Scott Wisniewski