views:

133

answers:

3

Bckground
I have a networked application written in C#. my server program has a UI and several communication threads, that read from tcp sockets and display messages on controller UI.

Communication with each client is done through a seprate thread. When i recieve some stream of messages from one client , the thread for that client writes on UI, which is a richtextbox on a Form.

I call SetTextHelper(string text) method of the form.

which looks like this

private delegate void MyTextUpdateHandler(string text);
public void SetTextHelper(string text)
{
   BeginInvoke(new MyTextUpdateHandler(SetText), new object[] { text });
}

public setText(string text)
{
   richtext.Text+= text;
}

Question
- If i use BeginInvoke my UI is entirely unresponsive when i m writing large stream of data to UI - Invoke solves that problem, but i read that for multi threaded environment where many thereads are sharing same resource Invoke can lead to deadlocks I share the common ichtextbox between around 16 threads - What would be a good desing for my situation?

A: 

You're probably simply spending all your time setting the text.

Try replacing the RichTextBox with an ordinary TextBox; it should be much faster.

SLaks
+2  A: 

Invoke shouldn't lead to deadlocks unless you own a lock while you're doing the invoking... but BeginInvoke shouldn't make the UI unresponsive either. Could the problem be that you're simply trying to update the textbox too often? Perhaps buffer the incoming text more? For example, only update the UI once every half second rather than any time you read any data.

Jon Skeet
Note that he isn't appending.
SLaks
@SLaks: He could well be appending within the code which is calling `SetTextHelper` though. It's basically unclear why the UI is unresponsive at the moment, but I *suspect* it's just overloaded with data.
Jon Skeet
yes i am appending the data to the richtextbox. but i m confused why Invoke makes it work correctly while BeginInvoke not? The text i m appending every time to the rich textbox is very small though, around 64 characters
Kazoom
@Kazoom: `Invoke` will be waiting for the UI to be updated before it continues - so that may be effectively slowing down the rate at which you're receiving data. We'd need to know more about what you're doing though.
Jon Skeet
@jon i m not doing anything more than this. i tried by writing a forever loop which prints lots of text and tried calling invoke and begininvoke and i had similar impact of begininvoke breaking the whole UI while invoke working properly
Kazoom
@Kazoom: Well yes, if you've got an infinite loop which is feeding it data as quickly as it possibly can, that's going to overload the UI event loop if you use `BeginInvoke`.
Jon Skeet
@Jon do u know why is it so? why would an asynchronous call add an overhead while a synchronous one wont?
Kazoom
@Kazoom: It's not a matter of adding an overhead - it's a matter of being self-limiting. When you call `Invoke` you're limiting the rate at which you're supplying data to the rate at which you can update the UI. When you call `BeginInvoke` you're overwhelming it.
Jon Skeet
@jon, thanks, one last question is Invoke thread safe, meaning if multiple threads are trying to write to UI component calling Invoke, does it ensure threadsafety?
Kazoom
@Kazoom: Yes, all the calls will be marshalled to the UI thread sequentially.
Jon Skeet
+2  A: 

You are calling BeginInvoke() too often. The UI thread will dispatch the delegate before it does its normal duties, like painting controls and responding to the mouse. If the delegate target is doing a lot of work (appending text to an RTB is expensive) or you are simply flooding it with requests, it doesn't get around to doing its normal job anymore. The UI will freeze. That happens pretty easily, about a 1000 invokes per second is enough.

The key to solve this is to realize that it is wasted effort to invoke so often. The human eye can't perceive updates faster than 25 times per second. So buffer the text until 50 milliseconds have passed since the last update. Environment.TickCount is a cheap timer.

Also beware that you might actually produce text faster than can be appended to the RTB. That requires throttling the thread. That's easy: use Invoke instead of BeginInvoke.

Hans Passant
ok i will try doing that, but why should i not get the unresponsiveness with BeginInvoke and not Invoke?
Kazoom