[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 492
  • Last Modified:

C++ sendng messages to GUI controls from different threads (thread - safety)

Hi Experts,
I would like to set my mind straight on this once and for all.  When calling GUI controls from other threads, I know you have to send messages.  Can thIs be the case for non-GUI objects?

As an example to help answer this question, I'm writing a progress bar control that I want to have the method "SetCurrentProgress(CString strText, double dPercentProgress)".  I primarily want to know how to make this function thread-safe, and easily accessible to my whole application (I guess making it global is the easiest/best way for easy access?)

My usual approach is to have a SendMessage call within the SetCurrentProgress function so that the message is sent to the window of the progress bar and ends up being on the GUI thread instead of whatever the calling thread is on.  But my progress bar doesn't update.  So I try doing MessageQueueHelper::DoEvents to force the events in the event loop to occur, and this seems problematic as well.....  I've had this kind of thing more or less working nicely before, but I really need to know the best way, and understand what's going wrong exactly when things don't seem to work.

Thanks for your help!
Mike
0
thready
Asked:
thready
  • 4
2 Solutions
 
HooKooDooKuCommented:
The key to being able to send a message to an object is that the object has to be associated to a  message loop.  From what I understand, any window with a window handle is going to be associated with a message loop.  But when you're talking about theads, there are "worker threads" and "UI threads".  Worker threads do not have a message loop.  A UI thread will have a message loop... it might or might not have a window or other GUI object associated with it.

As for things being thread safe, that's just the process of using thread synchronization objects to allow multiple threads access to the same piece of memory without stepping on one another.  Somethines thread synchronization is needed, sometimes it is not.

For example, let's say you had a thread with an abort boolean.  The perpose of the abort is to allow an outside process to set the abort flag.  The thread would periodically check to see if the abort flag had been turned on or not and terminate the thread if it was ever on.  Such a variable would not need to be made thread safe because you are only trying to set the value.  It's intended purpose isn't for one thread to set the flag and another thread reset it.  So it doesn't matter which thread sets the variable first.

But what about a variable that held the current progress of the progress bar.  Such a variable would need to be thread safe if an outside thread increments it (as opposed to seting the value).  To increment the value, the thread would need to read the variables current value, add one to the value, and write the new value back into the variable.  But if two thread try to do that at the same time, they might only increment the value by 1 when each thread should have incremented the value by 1.  In other words, both thread could read the current value, both add 1 to that value, and both write the same value back.  Even though both threads tried to increment the value, because they both did so at the same time, the value only increased by 1.

Now for the DoEvents:  That is a way of telling code that is running with a message loop to go process messages in the message loop.  After all, if you have a subroutine that runs a for loop from 1 to 1 million, while the code stays in that for loop, no messages (such as drag the window to a new location) get processed because the thread is busy in the for loop.  If you periodically insert DoEvents in the for loop, the loop will pause to process any messages pending in the message loop.
0
 
Todd GerbertIT ConsultantCommented:
My understanding is that SendMessage immediately & directly invokes the target's WndProc, where as PostMessage asks Windows to put a message in the target window's queue (which will later be retrieved by the target via the normal GetMessage/DispatchMessage) - so the latter may be necessary when sending messages between threads.
0
 
threadyAuthor Commented:
You're right tgerbert- there will often be situations where SendMessage will block and cause deadlocks.  Thanks HooKooDooKu, your answer is very thorough, but there's the key element I'm still missing- where do I put the SendMessage call?  I'm creating a progress bar and I want to 1) have easy access throughout the program to the progress bar and 2) not have to worry about calling PostMessage - I'd rather call a nice function that will call PostMessage for me...  This seems easy, but I always have some trouble with it.  Like now, I have my progress bar as a global variable and the program is crashing in RegisterWindowClass (custom control stuff) on the call to AfxGetInstanceHandle() - because current instance handle is null by making the progress bar global...  I don't understand.

So I revert to using a class variable in the parent dialog instead of making the progress bar global and now I have to worry about how I need to give my thread access to this-- which I find makes things a mess.  Globals are not the most kosher thing in the world, but they are definitely cleaner than the alternative.  After all, my app only has 1 progress bar.  Anyway.  So now I'm trying to figure out how to give this thread access to my progress bar in the most elegant way possible.

Thread (for testing my control)
    for(int i = 0; i < 100; i++)
         SomehowGetProgressPointer->SetCurrentProgress(i);

inside ProgressBar::SetCurrentProgress(int nProgress)
        PostMessage(WM_PROGRESS_UPDATE, 0, nProgress)

ON_MESSAGE(WM_PROGRESS_UPDATE, OnProgressUpdate)

LONG OnProgressUpdate(WPARAM wParaam, LPARAM lParam)
        m_nProgress=  (int)lParam;
        Invalidate();

Does this make sense?  Is there a better way?  How do I get access to the progress control in a clean way?

Sorry for the long-winded question - but this has been bugging me for far too long and I really want to make this clear for myself.

Thanks a lot for your help with this gentlemen!

Mike
0
NEW Veeam Agent for Microsoft Windows

Backup and recover physical and cloud-based servers and workstations, as well as endpoint devices that belong to remote users. Avoid downtime and data loss quickly and easily for Windows-based physical or public cloud-based workloads!

 
threadyAuthor Commented:
There is one alternative that I currently use that I don't like.  I just send messages and I keep a pointer to the window handle of the object that handles the message...   I just wish there were a better way....
0
 
threadyAuthor Commented:
... a more object oriented way...
0
 
threadyAuthor Commented:
After reading more about this online, I think it's actually better to pass HWNDs instead of accessing actual objects - it's a better way to decouple the GUI from the work being done.  So this ends this question and I'm satisfied.  Thanks again!
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now