views:

88

answers:

2

I have a Camera class that produces very large images at a high FPS that require processing by a ImageProcessor class. I also have a WPF Control, my View, that displays this information. I need each of these components needs to run on it's own thread so it doesn't lock up the processing.

Method 1) Camera has an Action<Image> ImageCreated that ImageProcessor subscribes to. ImageProcessor has an Action<Image, Foo> ImageCreated that contains an altered Image and Foo results for the View to show.

Method 2) Camera has a threadsafe (using locks and monitors) ProducerConsumer to which it produces Images, and ImageProcessor waits and Consumes. Same story for the View.

Method 2 is nice because I can create and manage my own threads.

Method 1 is nice because I have have multiple ImageProcessors subscribed to the Camera class. But I'm not sure who's thread is doing the heavyweight work, or if Action is wasting time creating threads. Again these images come in many times per second.

I'm trying to get the images to my View as quickly as possible, without tying up processing or causing the View to lock up.

Thoughts?

+2  A: 

Unless you do it yourself, using Method 1) does not introduce any multithreading. Invoking an action (unless you call BeginInvoke) does so synchronously, just like any normal method call.

I would advocate Method 2). There is no need to tie it to one single consumer. If you use this queue as a single point of contact between X cameras and Y processors, you've decoupled the cameras from the processors and could modify the value of X and Y independently.

EDIT

At the risk of being accused of blog spam here, I remembered that I wrote a component that's similar (if not an exact match) for what you're looking for awhile ago. See if this helps:

ProcessQueue

The gist of it is that you provide the queue with a delegate that can process a single item--in your case, Image--in the constructor, then call Start. As items are added to the queue using Enqueue, they're automatically dispatched to an appropriate thread and processed.

For example, if you wanted to have the image move Camera->Processor->Writer (and have a variable number of each), then I would do something like this:

ProcessQueue<Foo> processorQueue = new ProcessQueue<Foo>(f => WriteFoo(f));
ProcessQueue<Image> cameraQueue = new ProcessQueue<Image>(i => processorQueue.Enqueue(ProcessImage(i)));

You could vary the number of threads in cameraQueue (which controls the image processing) and processorQueue (which controls writing to disk) by using SetThreadCount.

Once you've done that, you would just call cameraQueue.Enqueue(image) whenever a camera captured an image.

Adam Robinson
At the moment I'm using something much simpler. See the ProducerConsumer class here: http://www.yoda.arachsys.com/csharp/threads/deadlocks.shtml With modifications for generic T instead of boxing/unboxing. I'm going to work on implementing some of your functionality. Thanks!
bufferz
"There is no need to tie it to one single consumer." What would you suggest if I wanted both an ImageProcessor and DiskWriter to consume the images produced by Camera? Remember that images stream constantly and are quite large. I am on a quad core machine so dedicated threads makes sense, I'm just struggling with the high level design. All of the .Net parallelism, from my limited understanding, seems to be suited for relatively quick operations and incur a lot of overhead.
bufferz
I would assume that you'd want the `ImageProcessor` to consume it, then the `DiskWriter`, correct? I would suggest either combining the two into a single call (meaning that you can't process another image with that processor until the image writes to the disk), or you could set up an additional queue that the processors populate and the writers consume. I'll post an example of how I'd do that using the component I wrote in a few minutes.
Adam Robinson
+1  A: 

Method one will not work - the Action<T> will executed on the thread that invoked it. Although you should probably use events instead of plain delegates in scenarios like this.

Method two is the way to go, but if possible you should use the new thread-safe collection of .NET 4.0 instead of doing the synchronization yourself - we all know how hard it is to get even the simplest multi-threaded code correct.

Daniel Brückner
Thanks for the tip about Action being "executed on the thread that invoked it." That helps me out a lot. Thanks!
bufferz
@bufferz: It's worth noting that this is true of *any* delegate type, not just `Action`. Unless you call `BeginInvoke`, it will execute as a normal method call, meaning inline on the same thread that calls it.
Adam Robinson