|
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.
|
|
|
|
|
Cedric Moonen wrote: 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.
Absolutely.
But, as Luc also made a point of, it's not necessary to update the UI faster than the user can cope with.
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|
IMO my timer-based GUI update suggestion did not get adopted because it got called "polling" at one point (mind you, not by me) and "polling is bad" as we all know. But I would rather do something every 100 msec all the time, than have it requested by some background threads as often as they can.
And obviously, one can start and stop the "polling" timer at a higher level (say when the background threads start/stop). So "smart polling" could avoid a lot of trouble and save many many CPU cycles.
|
|
|
|
|
Luc Pattyn wrote: IMO my timer-based GUI update suggestion did not get adopted because it got called "polling" at one point (mind you, not by me) and "polling is bad" as we all know. But I would rather do something every 100 msec all the time, than have it requested by some background threads as often as they can.
Agreed.
I suggested something similar in this post above[^] where I actually used the phrase "poll".
There's no point in updating the UI faster than the user has a remote chance of keeping up with. I would have the worker thread do more work before it "reports"; I mean if you hire a carpenter to do a job, you don't expect him to tell you "now I've picked up my screwdriver" and so on. You want to know when the job is done, or if he for some reason cannot do the job.
(Another one of my carpenter analogies.... )
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|
Cedric,
Thanks for the response. When I wrote, reapint the window, I meant by that was to generate a WM_PAINT message. Polling seems inefficient to me and some what backwards. However, I suspect that in many cases it is the way to go.
Bob
|
|
|
|
|
Okay, but I think you should think real hard about what a "simulation" is.
Again, this is based on the little information you've given, but I would consider the process of doing the 10,000 times repetitive thing a "simulation". When that work is done, in the worker thread, you alert the UI thread and display the results. During the computation you have some UI object, such as a progress bar, to inform the user that his "financial process" is being simulated.
Regarding your question about problems with your code snippet, I think Cédric made a good point, apart from the WM_PAINT stuff.
But while we're at it, I recommend you to not use the MFC versions of synchronisation objects like CCriticalSection, read more here[^].
And you would also benefit from reading this[^].
"It's supposed to be hard, otherwise anybody could do it!" - selfquote "High speed never compensates for wrong direction!" - unknown
|
|
|
|
|