Insomniac1257

I have what I assume is a very simple threading synchronization problem that I just can't figure out.

I have several forms that make up the UI that needs to be responsive. I also receive many updates from a background thread (100's per second) that sends data to the various forms. I use a handler that keeps a list of all the forms. Here's some psuedo code:

Handler.Update(Object Data)

{

lock(FormsLock)

{

for(int i=0; i < Forms.Count; i++)

Forms.AcceptData(Data);

}

}

The following function updates the UI so I invoke it on the owner's thread.

Form.AcceptData(Object Data)
{

if(this.InvokeRequired)

this.Invoke(AcceptDataDelegate, Data);

else

// Do work...

}

That code works fine by itself. But say I want to add another form to the collection that gets updated like this:

Handler.AddForm(Form NewForm)

{

lock(FormsLock)

{

Forms.Add(NewForm);

}

}

It's important to note that Handler.AddForm() is being called by one of the forms being updated. It's easy to see that if I'm getting 100's of updates that I am almost guaranteed a deadlock within a second. Any suggestions

Thanks in advance for the help.




Re: Visual C# General Threading synchronization

Peter Ritchie

I would suggest performing the loop on the GUI thread (which would be the same thread for all forms, so you can pick an form to use Invoke/BeginInvoke on).

I'm unclear what you problem is, are you getting a deadlock within a second






Re: Visual C# General Threading synchronization

Insomniac1257

I am getting a deadlock almost immediately whenever AddForm() is called.

AcceptData() is called from a seperate thread. Handler is a static class so I cannot use Invoke() there. My only other option would be to subscribe/unsubscribe each individual form to the same event that Handler.AcceptData() is subscribed to. I thought I could avoid the overhead (if indeed there is any) by only having the static class subscribed to the event.

I've also ran into problems when the form closes and the event fires before I unsubscribe to it. Another reason I avoided that approach.






Re: Visual C# General Threading synchronization

OmegaMan

Don't know if this helps....I had a program that used to monitor incoming UDP socket for specific messages from multiple internal webservices on different servers. That program would have to dynamically create and push data to multiple tabs which would then route the data to into various controls upon the tab page.

I found that by creating a timer and having the timer collect the data and do the creation of the tabs and subsequent data distribution processing was the most effective. Any callbacks or invokes would only push data onto queues which could be locked for thread safety and those queues would be emptied only by the timer data manager. That way only one process is responsible for pushing data towards the GUI, it was a centralized Flight Tower so to speak and that would kick off to an internal drum beat.





Re: Visual C# General Threading synchronization

mwalts

Interesting, I guess you could spawn each update as a seperate thread and use a Monitor.Wait(FormsLock); in the form(s) that call AddForm and signal them after the loop... but creating that many threads (even short lived ones) might bring your system to it's knees.

Looks like you need to redesign your mechanism somewhat...
Here is a very nice (albeit long) explanation of threading in .NET that might spark some ideas for you
http://www.yoda.arachsys.com/csharp/threads/index.shtml

Good luck,

-mwalts




Re: Visual C# General Threading synchronization

Peter Ritchie

I don't see how you could get a deadlock there. AddForm() is never called via Control.Invoke

It could simply be starvation, the AcceptData() loop could be stealling all the CPU and the AddForm() lock never gets a chance to lock; but that seems suspect.

If one thread is reading the data and the other is writing it, you might be better off with a reader-writer-lock that queue's and prioritizes locking. There's ReaderWriterLock but Jeffery Richter's version is much better.






Re: Visual C# General Threading synchronization

mwalts

Yeah, that is what I thought when I first inspected the code, because all locks happen in one thread, if the thread has the lock it shouldn't block.

I'm not sure how that works when you call another thread and it calls you back though. Should still work I just wouldn't put money on it... might be better the have a return value from the form update that is perhaps an enumerator which you could check to see which internal calls need to be made. At the very least it would allow you to isolate the problem a bit better.

-mwalts




Re: Visual C# General Threading synchronization

Insomniac1257

I don't see what's so hard to understand....

All locks do not happen on the same thread.






Re: Visual C# General Threading synchronization

Insomniac1257

AddForm() doesn't need to be called by Control.Invoke(). AddForm() is called by the main UI thread.




Re: Visual C# General Threading synchronization

Insomniac1257

To me it seems like a very simple case where locking an object simply doesn't work. And the mechanism does need to be redesigned. But how




Re: Visual C# General Threading synchronization

Insomniac1257

Let me try to explain more clearly.

AcceptData() receives a call from a thread that is not the main UI. it locks the object, and then starts calling all the Form.AcceptData().

Meanwhile I (the user) click on a button to create another form.

AddForm() is now called before Handler.AcceptData() finishes.

Now AddForm(), whom is on the main UI thread, is now waiting for the lock to be released.

At the same time Form.AcceptData() is trying to Invoke() on the main thread but the main thread is busy waiting for the lock.

Seems like a very simple synchronization case to me. I spotted the deadlock the instant it happened. I just don't know how to better design it.






Re: Visual C# General Threading synchronization

Peter Ritchie

Insomniac1257 wrote:

Let me try to explain more clearly.

AcceptData() receives a call from a thread that is not the main UI. it locks the object, and then starts calling all the Form.AcceptData().

Meanwhile I (the user) click on a button to create another form.

AddForm() is now called before Handler.AcceptData() finishes.

Now AddForm(), whom is on the main UI thread, is now waiting for the lock to be released.

At the same time Form.AcceptData() is trying to Invoke() on the main thread but the main thread is busy waiting for the lock.

Seems like a very simple synchronization case to me. I spotted the deadlock the instant it happened. I just don't know how to better design it.

Okay, you've hit the classic Control.Invoke deadlock. You've locked your GUI thread which means all calls to Control.Invoke will block as well, if both of those rely on the same object for two different lock statements you're stuck.

A quick fix may be to use BeginInvoke instead of Invoke. This puts the call into the queue, waiting until the GUI thread is done doing any current work (like locking an object). But, I would suggest a bit of a redesign.

If all the "work" is forced (or needs) to happen on the GUI thread you don't have any need for locking, a single thread cannot be running twice. I would suggest again moving the entire AcceptData loop into the GUI thread (i.e. have a method that performs the AcceptData loop and Invoke that, rather than having an Invoke in AcceptData) and dispense with the locks... A little more work; but something like:

Code Snippet

Handler.Update(Object Data)
{
Forms[0].Invoke(new MyDelegate(ProcessData), new object[]{Data});
}

//...
// Form class(es)
void ProcessData(Object Data)
{
for(int i=0; i < Forms.Count; i++)
Forms.AcceptData(Data);
}

void AcceptData(Object Data)
{
// Do work...
}

But, this isn't an optimal design. I would suggest using AsyncOperationManager to ensure you maintain required thread affinity; but that can get really complicated really quickly.






Re: Visual C# General Threading synchronization

Insomniac1257

I will always have at least one form [0] I wonder why I didn't think of that

And I wouldn't need a lock because Invoke can't run while AddForm is running.

What's this about AsyncOperationManager I don't understand why this need be so complicated... to me it seemed like a classic case where locking an object would fail and I was hoping there was a simple workaround.

Your design works... as long as I access that object on one thread alone.

What if I wanted to do multi-threading How would I solve this problem

Thanks for the help.






Re: Visual C# General Threading synchronization

Peter Ritchie

Insomniac1257 wrote:

I will always have at least one form [0] I wonder why I didn't think of that

And I wouldn't need a lock because Invoke can't run while AddForm is running.

What's this about AsyncOperationManager I don't understand why this need be so complicated... to me it seemed like a classic case where locking an object would fail and I was hoping there was a simple workaround.

Your design works... as long as I access that object on one thread alone.

What if I wanted to do multi-threading How would I solve this problem

Thanks for the help.

You'll still have to marshal all control/form data access to the GUI thread, even if you use multithreading. I generally recommend only using one CPU-bound thread per CPU. the GUI Thread should not be CPU bound, so using a background thread for CPU intensive work still fits. If you wanted to have multiple threads all doing something that affects the GUI, you'll make your life miserable.

AsyncOperationManager is more complex because it tries to fulfill general thread affinity requirements; since you're dealing with forms you can use the InvokeRequired/[Begin]Invoke pattern which is far simpler. You'd basically do the same thing with AsyncOperationManager by calling Invoke on a delegate; but you have to do a bit of initialization (initialization you get for free with WinForms...).






Re: Visual C# General Threading synchronization

Insomniac1257

Unfortunately all these background updates require updating the main interface. Updating the interface from a worker thread certainly can't be new.

Is there a better [performance] way to do it

Honestly there's a very small amount of CPU usage involved. Every update mostly just changes the user interface... which I've found sucks an unbelievable amount of CPU even on the quad core system I'm developing on.

An application in C++ that does virtually the same thing eats about 2% of the CPU. I'm getting anywhere from 5% - 52%! Even with a small amount of updates (about 100/sec).