|
Hi,
IIRC some messages get special treatment; e.g. WM_PAINT messages may get merged if all they do is request a repaint of (parts of) the same Control. Maybe it is also true for setting a text to a label,
etc.
Your app is also likely to get some messages from the outside world (device inserted, task manager checking for "not responding", etc).
if you suspect your queue is "filling up", the design is not OK; what will happen if your system gets another load at the same time? what if your app needs to run on a slower PC?
When you have a progress bar showing a progress say in percent, then it does not really make sense to send it 10,000 progress updates, it probably can only show 100 or so different states anyway; so you should consider a cheaper way of reporting, probably by aggregating a number of progress steps.
Do you really need that much GUI activity; after all it is consuming its share of CPU cycles, and hence slowing down the simulation of which you are eager to know the progress!?!?
One way I often handle these kinds of requirements is by having the background doing its job without reporting anything, and having one more thread which periodically (a few Hz) reports the progress by
looking at the backgrounder state variables (if they are simple enough, that does not require any
synchronization, locking, whatever...)
|
|
|
|
|
Luc,
Thanks for the response. As it turns out, the problem is not any worse with a heavily loaded system. With sleep statements in the worker threads and a heavily loaded system, no messages are lost.
I am thinking that using the Window's messaging approach to return results is not the way to go. Please comment.
Bob
|
|
|
|
|
What we did to avoid this was to ...how to say... delay updates of the GUI. How to explain, if there were a 1000 things needed to be updated on the GUI, instead of sending 1000 messages towards the GUI we started putting them in a packet, if a packet had 100 objects in it, we would send this towards the GUI, if a packet had less than 100 objects in it but a given time had passed since the last time a packet was sent, we sent the not-full packet.
Also, as Luc Pattyn said, polling can also help solving your problem.
> The problem with computers is that they do what you tell them to do and not what you want them to do. <
|
|
|
|
|
Thanks for the response. However, my current requirements state that I cannot batch them up. However, I can work on getting that changed. Would do you think of the idea of return results by calling a method in GUI interface? Of course, there would have to be a lock on the method so that no two threads could update the GUI at the same time.
Bob
|
|
|
|
|
BobInNJ wrote: ...no two threads could update the GUI at the same time.
Secondary threads should not interact with IO components owned by the primary thread. You should post the message(s) to the primary thread instead.
"Love people and use things, not love things and use people." - Unknown
"The brick walls are there for a reason...to stop the people who don't want it badly enough." - Randy Pausch
|
|
|
|
|
You mean polling? The GUI thread periodically asking for the values needed?
> The problem with computers is that they do what you tell them to do and not what you want them to do. <
|
|
|
|
|
Thanks for the response. I do not mean polling. I mean that the worker threads calls a method in the GUI object to report the next result. This is how I originally wrote the code. The problem with this approach is that two threads could be updating the object at the same time. This can cause a problem. In fact, I posted this problem to the group earlier. There were two suggestions. One was to use a lock, the other was to use SendMessage. I choose the second option. I am not thinking I should have chosen the first.
Any comments?
Bob
|
|
|
|
|
I remember your post about this, but wouldn't that method that gets called update the GUI? If not, then i misunderstood you...if it doesn't actually fiddle with windows then the lock-aproach is better. However, if you want to fiddle with the gui, like redrawing counters, you either have to use messages as you tried (but if there are a lot of the messages, as you experienced, this can cludge the GUI) or use polling.
On a different aproach, you might place Sleep(0) or Sleep(1) in your secondary threads at the right places to give other threads the chanche to do their business but of course this might produce slowdown of the whole thing.
> The problem with computers is that they do what you tell them to do and not what you want them to do. <
|
|
|
|
|
BobInNJ wrote: ...the other was to use SendMessage.
Which was a bad suggestion.
"Love people and use things, not love things and use people." - Unknown
"The brick walls are there for a reason...to stop the people who don't want it badly enough." - Randy Pausch
|
|
|
|
|
I believe using SendMessage or PostMessage for my application is not the way to go. I am not saying that it was a bad suggestion just that in my case it did not work out.
Bob
|
|
|
|
|
What do you think of the idea of lowering the priority on the threads that are doing the computation? That way, I can be sure that GUI thread can keep up and the queue will not over flow?
Thanks
Bob
|
|
|
|
|
BobInNJ wrote: lowering the priority on the threads
Never alter the priority of any thread until you know exactly where your bottlenecks are by measuring, and even then it suggests a poorly designed application.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|
I have to agree with Roger Stoltz there, finetuning threads by changing priorities can be a very difficult task and it probably would lead to different reasults on different systems..
> The problem with computers is that they do what you tell them to do and not what you want them to do. <
|
|
|
|
|
I would like to say that I am learning a great deal about threads and locks in this discussion. I would like to thank everybody who responded to my posts. Here is the revised plan for the reporting function in the GUI interface:
<br />
void<br />
GUIClass::repFunction( struct ReportData repData )<br />
{<br />
if ( WaitForSingleObject(mutexHandle, INFINITE) == WAIT_OBJECT_0 ) {<br />
copy repData to this class<br />
add 1 to a counter<br />
if the counter % 4 == 0<br />
generate a WM_PAINT request<br />
end if<br />
ReleaseMutex(mutexHandle);<br />
}<br />
<br />
}
The data item mutexHandle is a member of the class GUIClass and is defined as follows:
HANDLE mutexHandle;
In addition, it is initialized in the constructor for GUIClass as follows:
mutexHandle = CreateMutex( NULL, FALSE, NULL );
Now, does anybody see any problems with my design? I am not convinced that I have the arguments
to CreateMutex right? One last thing, I have a prototype of my application, and the following reporting function is working in the prototype.
Thanks
Bob
|
|
|
|
|
BobInNJ wrote: I am thinking that I am overflowing the window’s message queue.
Doubtful, since you are bypassing it by using SendMessage() .
"Love people and use things, not love things and use people." - Unknown
"The brick walls are there for a reason...to stop the people who don't want it badly enough." - Randy Pausch
|
|
|
|
|
David,
Thanks for the response. How can I verify whether or not the message queue is full? Is there a call to find out how many elements are in the queue?
Bob
|
|
|
|
|
BobInNJ wrote: How can I verify whether or not the message queue is full?
Unless you suspect several thousand messages have been posted to a window's queue, that's not sometihng that an application would normally be concerned with. This may also be why you find little to no discussion about it on the Web.
"Love people and use things, not love things and use people." - Unknown
"The brick walls are there for a reason...to stop the people who don't want it badly enough." - Randy Pausch
|
|
|
|
|
David,
Thanks for the response. I believe that I am getting several thousand messages posted to the message queue. Currently, the threads are returning 1000 points each, by default. However, when run for real that number could be as high as 100,000. It seems to me that there should be a call that I can make to found out how big the queue is, and how many items there are in it. However, I cannot find such a call?
Do you still think that I am not over flowing the message queue?
Bob
|
|
|
|
|
What are you trying to do?
Have you really thought about how you've designed the application and what the requirements are, especially the timing requirements?
You've created two worker threads that do some kind of simulation and you want to alert the UI thread in order to display the result of the simulation, I guess.
When it comes to secondary threads you should never use ::SendMessage() because you may deadlock your application if the main thread doesn't pump messages. Use ::PostMessage() instead. David has already advised you regarding this matter.
Now to what I believe is the real problem if I may read a little between the lines of your posts....
Worker threads are mostly used when you have some kind of computing to do that takes a lot of time when you don't want your user interface to freeze up. When the computation is done you alert the UI thread by posting a user defined message.
Since you believe that you choke the UI thread with messages, I guess the simulation is done rather fast and thus would not need a secondary thread. Either that or your UI thread doesn't process messages the way it's supposed to.
It sounds like your simulation takes a lot less time than displaying the results of it which means that you choke the UI thread; for every message it processes it will get more than one in the queue and it's all downhill from there. Putting calls to ::Sleep() does not in any way solve the problem, it simply disguises it.
From my point of view this issue is more about design than how big the message queue is, or can be.
To be able to give a more accurate suggestion you have to provide more information about what you're trying to do; what is a simulation, how long does it take to perform a simulation, what kind of data is displayed and so on.
But based on the information you've given so far I would suggest the following:
Create a queue of your own that holds simulation results, e.g. std::queue . Protect the queue with a critical section since it will be read from the UI thread and written to from the worker threads. Let the UI thread periodically poll the queue and display the results.
Now this may be a solution that doesn't really meet the requirements of your application, but since you haven't mentioned them this can serve as a starting point.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|
Hi Roger,
from an earlier thread[^] I understood the two background threads perform simulations over and over again, and report back to the UI on every little simulation, hence every few milliseconds.
To me it does not make much sense to update the screen that often, the human eye can't cope, and most of the thread switching and messaging is just wasting CPU cycles.
|
|
|
|
|
Hi Luc,
Luc Pattyn wrote: and report back to the UI on every little simulation, hence every few milliseconds.
Mmmm, that's what I figured.
Luc Pattyn wrote: To me it does not make much sense to update the screen that often, the human eye can't cope
I agree completely and that was also one of my points when I mentioned the timing requirements. However, I may have expressed that point rather subtle...
Luc Pattyn wrote: most of the thread switching and messaging is just wasting CPU cycles.
Indeed.
Or as one from Chicago I had the privilege to work with a couple of years ago so colourfully said it: "it's just burning MIPS".
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|
Roger,
First, I want to thank you for your response. My program is simulated a financial process many times. My plan is to have one worker thread for each cpu or core on the machine. The basic outline is that the worker Thread does a simulation, reports the results to the GUI thread and then repeats the process. Typically, the process needs to be repeated about 10,000 times. However, this number is under user control and might be as high as 1,000,000. Each simulation takes about 10ms seconds depending on the data. At the moment, the amount of data it is reporting is about 16 bytes. This might increase a bit.
I am thinking that you are right, and that I need to redesign the application. My current thinking is not to use the Window’s message queue but instead have the worker thread call a method in the user’s object. I am going to call this method, reportingMethod. Now, suppose that two threads call this method at the same time, then I am going to have a problem. Therefore, I need to protect myself from this situation. My plan is do the following:
<br />
GUIClass:: ReportingMethod ( struct Report rep )<br />
{<br />
CSingleLock singleLock( &m_CritSection );<br />
singleLock.Lock();<br />
<br />
copy the data<br />
increment counter<br />
if counter % 4 == 0<br />
repaint the window<br />
end if<br />
<br />
singleLock.Unlock();<br />
<br />
}<br />
In the above example, m_CritSection is of type CCriticalSection and a member of the class GUICLASS. Do you or anybody else see a problem with this approach? Do I have the locking code right?
Bob
|
|
|
|
|
BobInNJ wrote: Do you or anybody else see a problem with this approach?
Yes, with the repaint the window part. Depending on the way you plan to do this, this may causes problems because you can't access GUI objects from another thread than the one they were created in.
In fact, a much better approach is quite similar as what you did: keep your function as it is but remove the repaint the window part. Instead simply send a WM_PAINT message to your window that will force a redraw. In the OnPaint handler, you can then access the data you sent. Of course, there you also have to protect the access using critical sections.
If the repainting part is much slower than the run of the simulation, you can have another approach: don't send a WM_PAINT message in the ReportingMethod function but instead create a timer in your UI thread. Whenever your timer fires, you repaint your window.
|
|
|
|
|
I agree, but...
Cedric Moonen wrote: send a WM_PAINT message to your window that will force a redraw.
O-oh, Cédric...
You must never send or post a WM_PAINT message. It's not a "real" message.
It works the same way as the WM_TIMER message; it never enters the message queue of the application.
The WM_PAINT message gets generated by the system upon calls to e.g. UpdateWindow() . Before generating the WM_PAINT message the clipping region is computed and there's also the BeginPaint() /EndPaint() requests that are skipped if WM_PAINT would be sent instead or being generated.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|
Mmh, yes, I was not sure about this so thanks for correcting me (in fact I never really played directly with WM_PAINT messages).
Well, then I think a better approach would be to send a user defined message and in the handler of this message, call Invalidate() to force the redraw.
|
|
|
|
|