Help with threads
-
The less hassle free solution is sending a message from the worker thread to the ui thread by using
SendMessage()
orPostMessage()
with one of your windows.Thank you for the suggestion, but I am unfamiliar with SendMessage() and PostMessage(). Would you be able to give me an example of how to do this or direct me to a good tutorial/article?
-
Thank you for the suggestion, but I am unfamiliar with SendMessage() and PostMessage(). Would you be able to give me an example of how to do this or direct me to a good tutorial/article?
Its easy. Inside your program every window belongs to a thread - the thread that created the window (note that in a normal program every window is created by a single thread). That thread has a message queue. Every time you do something to the window - click on it with the mouse, request redrawing part of its client area - a message for that window is put in the message queue of the thread that handles that window. The gui thread should remove messages and process them in a loop (this is called the main message loop: GetMessage/DispatchMessage funcionts). In your case MFC does this for you, in pure win32 you do that for yourself. The window messages (WM_LBUTTONDOWN, WM_CLOSE, WM_PAINT, etc...) are also messages that come from that message queue, and when the gui thread encounters such a window message it dispatches that to the windowproc of the right window. OK, how is this related to worker threads? Actually message queues are one of the best things to use as inter-thread communication and fortunately this thread-message queue is thread safe, you can put a message to it from any other thread. For example you can put a window message into the message queue of the gui thread by calling PostMessage() or SendMessage() form your worker thread. Both of these functions put a message to the message queue of the gui thread but PostMessage() returns immediately while SendMessage() blocks the execution of the caller thread and returns only after the gui thread has processed the message. OK, but where to send the message, and what message??? You should define your own window messages by starting from the WM_APP constant. You define for example WM_APP+0 and WM_APP+1 and so on as your thread messages. With PostMessage() and SendMessage() you can send these custom messages to your main window and you can also specify 2 integer parameters: wParam and lParam (lParam is somtimes used as a pointer). For example when you are copying a big file on the worker thread and the percentage counter changes you can use PostMessage() to send a WM_APP+0 message with a wParam=percentage parameter. In your main window you handle the WM_APP+0 message (on your gui thread) and update your progressbar accordingly. Note that we used PostMessage() that put the message to the queue of the gui thread for later processing and PostMessage() returned immediately with
-
Its easy. Inside your program every window belongs to a thread - the thread that created the window (note that in a normal program every window is created by a single thread). That thread has a message queue. Every time you do something to the window - click on it with the mouse, request redrawing part of its client area - a message for that window is put in the message queue of the thread that handles that window. The gui thread should remove messages and process them in a loop (this is called the main message loop: GetMessage/DispatchMessage funcionts). In your case MFC does this for you, in pure win32 you do that for yourself. The window messages (WM_LBUTTONDOWN, WM_CLOSE, WM_PAINT, etc...) are also messages that come from that message queue, and when the gui thread encounters such a window message it dispatches that to the windowproc of the right window. OK, how is this related to worker threads? Actually message queues are one of the best things to use as inter-thread communication and fortunately this thread-message queue is thread safe, you can put a message to it from any other thread. For example you can put a window message into the message queue of the gui thread by calling PostMessage() or SendMessage() form your worker thread. Both of these functions put a message to the message queue of the gui thread but PostMessage() returns immediately while SendMessage() blocks the execution of the caller thread and returns only after the gui thread has processed the message. OK, but where to send the message, and what message??? You should define your own window messages by starting from the WM_APP constant. You define for example WM_APP+0 and WM_APP+1 and so on as your thread messages. With PostMessage() and SendMessage() you can send these custom messages to your main window and you can also specify 2 integer parameters: wParam and lParam (lParam is somtimes used as a pointer). For example when you are copying a big file on the worker thread and the percentage counter changes you can use PostMessage() to send a WM_APP+0 message with a wParam=percentage parameter. In your main window you handle the WM_APP+0 message (on your gui thread) and update your progressbar accordingly. Note that we used PostMessage() that put the message to the queue of the gui thread for later processing and PostMessage() returned immediately with
Good comment, but you may win an award for the most poorly formatted comment this year. :)
-
Its easy. Inside your program every window belongs to a thread - the thread that created the window (note that in a normal program every window is created by a single thread). That thread has a message queue. Every time you do something to the window - click on it with the mouse, request redrawing part of its client area - a message for that window is put in the message queue of the thread that handles that window. The gui thread should remove messages and process them in a loop (this is called the main message loop: GetMessage/DispatchMessage funcionts). In your case MFC does this for you, in pure win32 you do that for yourself. The window messages (WM_LBUTTONDOWN, WM_CLOSE, WM_PAINT, etc...) are also messages that come from that message queue, and when the gui thread encounters such a window message it dispatches that to the windowproc of the right window. OK, how is this related to worker threads? Actually message queues are one of the best things to use as inter-thread communication and fortunately this thread-message queue is thread safe, you can put a message to it from any other thread. For example you can put a window message into the message queue of the gui thread by calling PostMessage() or SendMessage() form your worker thread. Both of these functions put a message to the message queue of the gui thread but PostMessage() returns immediately while SendMessage() blocks the execution of the caller thread and returns only after the gui thread has processed the message. OK, but where to send the message, and what message??? You should define your own window messages by starting from the WM_APP constant. You define for example WM_APP+0 and WM_APP+1 and so on as your thread messages. With PostMessage() and SendMessage() you can send these custom messages to your main window and you can also specify 2 integer parameters: wParam and lParam (lParam is somtimes used as a pointer). For example when you are copying a big file on the worker thread and the percentage counter changes you can use PostMessage() to send a WM_APP+0 message with a wParam=percentage parameter. In your main window you handle the WM_APP+0 message (on your gui thread) and update your progressbar accordingly. Note that we used PostMessage() that put the message to the queue of the gui thread for later processing and PostMessage() returned immediately with
-
Good comment, but you may win an award for the most poorly formatted comment this year. :)
Well, formatting has never been a strength of mine... :-)
-
The less hassle free solution is sending a message from the worker thread to the ui thread by using
SendMessage()
orPostMessage()
with one of your windows.pasztorpisti wrote:
The less hassle free solution is sending a message from the worker thread to the ui thread by using
SendMessage()
...What happens when the primary (UI) thread is blocked and the worker thread sends it a message via
SendMessage()
?"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Show me a community that obeys the Ten Commandments and I'll show you a less crowded prison system." - Anonymous
-
pasztorpisti wrote:
The less hassle free solution is sending a message from the worker thread to the ui thread by using
SendMessage()
...What happens when the primary (UI) thread is blocked and the worker thread sends it a message via
SendMessage()
?"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Show me a community that obeys the Ten Commandments and I'll show you a less crowded prison system." - Anonymous
Then the worker thread is waiting until the message is processed on the gui thread. Exploiting this you can for example pop up a messagebox from the worker thread by sending a WM_APP+X message to the gui thread that processes WM_APP+X and "blocks" until the message box is closed. The worker thread continues running only after the message box is closed and the processing of WM_APP+X is finished on the gui thread. I quoted "blocks" because a MessageBox call doesn't really block the gui thread, it just blocks the processing of a single message an it runs an inner messageloop of its own so processing other messages from the queue of the gui thread goes on. For example your windows are still drawn (by WM_PAINT messages) while a messagebox is active. PS: The ui thread should never be blocked, that causes unresponsive UI. Often thats the reason for multithreading and not gaining performance. If your UI thread is blocked then your program is either buggy or poorly designed.
-
Then the worker thread is waiting until the message is processed on the gui thread. Exploiting this you can for example pop up a messagebox from the worker thread by sending a WM_APP+X message to the gui thread that processes WM_APP+X and "blocks" until the message box is closed. The worker thread continues running only after the message box is closed and the processing of WM_APP+X is finished on the gui thread. I quoted "blocks" because a MessageBox call doesn't really block the gui thread, it just blocks the processing of a single message an it runs an inner messageloop of its own so processing other messages from the queue of the gui thread goes on. For example your windows are still drawn (by WM_PAINT messages) while a messagebox is active. PS: The ui thread should never be blocked, that causes unresponsive UI. Often thats the reason for multithreading and not gaining performance. If your UI thread is blocked then your program is either buggy or poorly designed.
pasztorpisti wrote:
Then the worker thread is waiting until the message is processed on the gui thread.
I'm not talking about the primary thread being busy for a few nanoseconds. I'm talking about deadlock. For example, the secondary thread sends a "add item to the control" message (e.g.,
LB_ADDSTRING
) to the primary thread, which is blocked waiting on the secondary thread to complete. Now the secondary thread cannot complete because it is no longer running (waiting onSendMessage()
to return). This is the primary reason whySendMessage()
should rarely, if ever, be used to communicate between primary and secondary threads."One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Show me a community that obeys the Ten Commandments and I'll show you a less crowded prison system." - Anonymous
-
pasztorpisti wrote:
Then the worker thread is waiting until the message is processed on the gui thread.
I'm not talking about the primary thread being busy for a few nanoseconds. I'm talking about deadlock. For example, the secondary thread sends a "add item to the control" message (e.g.,
LB_ADDSTRING
) to the primary thread, which is blocked waiting on the secondary thread to complete. Now the secondary thread cannot complete because it is no longer running (waiting onSendMessage()
to return). This is the primary reason whySendMessage()
should rarely, if ever, be used to communicate between primary and secondary threads."One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Show me a community that obeys the Ten Commandments and I'll show you a less crowded prison system." - Anonymous
The gui thread should never block, as a consequence it should never wait for the worker thread to complete. When the worker thread completes it can send/post a message to the gui thread to signal. Why would you want a blocking-wait on the gui thread???
-
pasztorpisti wrote:
Then the worker thread is waiting until the message is processed on the gui thread.
I'm not talking about the primary thread being busy for a few nanoseconds. I'm talking about deadlock. For example, the secondary thread sends a "add item to the control" message (e.g.,
LB_ADDSTRING
) to the primary thread, which is blocked waiting on the secondary thread to complete. Now the secondary thread cannot complete because it is no longer running (waiting onSendMessage()
to return). This is the primary reason whySendMessage()
should rarely, if ever, be used to communicate between primary and secondary threads."One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Show me a community that obeys the Ten Commandments and I'll show you a less crowded prison system." - Anonymous
DavidCrow wrote:
For example, the secondary thread sends a "add item to the control" message (e.g.,
LB_ADDSTRING
) to the primary threadThis is the scenario that's confused me from time to time. It is really legal for a thread to do a SendMessage() to a control (the target of the LB_ADDSTRING) that is owned by another thread? I thought that Windows checked "thread ownership" of the control and returned an error on the SendMessage(). If the "primary thread" is, as is implied in your reply, the UI thread, then the "secondary thread" should not be allowed access to the control. I've always used "PostMessage()" and used "user defined messages" to have secondary threads pass messages / commands to the primary thread for action. Since "PostMessage()" is just a queueing action, there is no deadlock (although you might not get the immediate feedback of a screen update). Does anyone have the definitive answer on "SendMessage and Control Owner Thread" question?
-
The less hassle free solution is sending a message from the worker thread to the ui thread by using
SendMessage()
orPostMessage()
with one of your windows.Ok, instead of modifying my original question I will continue the thread since it has been really helpful. So, my program uses the following code to create a new thread.
m_Thread = AfxBeginThread(Acq_Data,this,THREAD_PRIORITY_HIGHEST);
This thread is told to look at the current class and the controlling function is
Acq_Data
. InAcq_Data
there is the following code.while(dlg->m_Continue && ! dlg->m_inst->m_Flags.Halt)
{
dlg->m_inst->ADCdbInquire(dlg->m_halfdata,hdlg,MSG_DRAW_SPECTRUM);
}The
ADCdbInquire()
function contains a call toPostMessage()
bool CPXI::ADCdbInquire(short *halfbuffer, HWND hwnd ,int Message)
{
short iStatus, iHalfReady, iDAQstopped = 0;
unsigned long ulPtsTfr=1500; // number of points transferred// Are you ready? iStatus = DAQ\_DB\_HalfReady(m\_6052\_Device, &iHalfReady, &iDAQstopped); if ((iHalfReady == 1) && (iDAQstopped == 0)) { iStatus = DAQ\_DB\_Transfer(m\_6052\_Device, halfbuffer, &ulPtsTfr, &iDAQstopped); NIDAQErrorHandler(iStatus, "DAQ\_DB\_Transfer",m\_SuppressErrors); if(Message != -1) PostMessage(hwnd,WM\_COMMAND,Message,NULL); return true; } return false;
}
and is sending a custom message defined in the header file.
#define MSG_DRAW_SPECTRUM (WM_APP + 5)
This custom messages refers to member functionOnDrawSpectrum()
where the following code exists to update the window.void CDlg_SpectrumAnalyzer::OnDrawSpectrum()
{int starti,finishi; starti = m\_datacount \* m\_BuffSize / 2; finishi = (m\_datacount + 1)\* m\_BuffSize / 2; if(finishi > m\_AcqNPts) finishi = m\_AcqNPts; int j = 0; for(int i = starti; i < finishi; i++) { m\_fftdata\[i\].re = m\_halfdata\[j++\]; m\_fftdata\[i\].im = 0.; } m\_datacount++; // if we are done collecting draw the graph if((m\_datacount == m\_NBuffs)) { OnSpectrumStart(); float freq = 0; float stepsize = ((float)(m\_MaxFreq)) / ((float)(m\_AcqNPts-1)/2.0); m\_datacount = 0; m\_Graph->ClearGraph(m\_graphNum,false); // do fft fftw\_one(m\_the\_plan, m\_fftdata, NULL); //m\_AcqNPts should be 2^n therefore /2 should be integer for(int i = m\_AcqNPts-1; i >= m\_AcqNPts/2;
-
The less hassle free solution is sending a message from the worker thread to the ui thread by using
SendMessage()
orPostMessage()
with one of your windows.Actually,
SendMessage
is synchronous... so if it's threaded, it really should be aPostMessage
, otherwise you'll tie up the worker thread waiting on the GUI update. -
Good comment, but you may win an award for the most poorly formatted comment this year. :)
Yeah that was pretty painful :)
-
DavidCrow wrote:
For example, the secondary thread sends a "add item to the control" message (e.g.,
LB_ADDSTRING
) to the primary threadThis is the scenario that's confused me from time to time. It is really legal for a thread to do a SendMessage() to a control (the target of the LB_ADDSTRING) that is owned by another thread? I thought that Windows checked "thread ownership" of the control and returned an error on the SendMessage(). If the "primary thread" is, as is implied in your reply, the UI thread, then the "secondary thread" should not be allowed access to the control. I've always used "PostMessage()" and used "user defined messages" to have secondary threads pass messages / commands to the primary thread for action. Since "PostMessage()" is just a queueing action, there is no deadlock (although you might not get the immediate feedback of a screen update). Does anyone have the definitive answer on "SendMessage and Control Owner Thread" question?
That's a good question... I do believe the framework allows you to do it though... although now that you're questioning it, you're making me question whether I remember that correctly.
-
The gui thread should never block, as a consequence it should never wait for the worker thread to complete. When the worker thread completes it can send/post a message to the gui thread to signal. Why would you want a blocking-wait on the gui thread???
SendMessage()
is a blocking call... that's what he was pointing out. -
Ok, instead of modifying my original question I will continue the thread since it has been really helpful. So, my program uses the following code to create a new thread.
m_Thread = AfxBeginThread(Acq_Data,this,THREAD_PRIORITY_HIGHEST);
This thread is told to look at the current class and the controlling function is
Acq_Data
. InAcq_Data
there is the following code.while(dlg->m_Continue && ! dlg->m_inst->m_Flags.Halt)
{
dlg->m_inst->ADCdbInquire(dlg->m_halfdata,hdlg,MSG_DRAW_SPECTRUM);
}The
ADCdbInquire()
function contains a call toPostMessage()
bool CPXI::ADCdbInquire(short *halfbuffer, HWND hwnd ,int Message)
{
short iStatus, iHalfReady, iDAQstopped = 0;
unsigned long ulPtsTfr=1500; // number of points transferred// Are you ready? iStatus = DAQ\_DB\_HalfReady(m\_6052\_Device, &iHalfReady, &iDAQstopped); if ((iHalfReady == 1) && (iDAQstopped == 0)) { iStatus = DAQ\_DB\_Transfer(m\_6052\_Device, halfbuffer, &ulPtsTfr, &iDAQstopped); NIDAQErrorHandler(iStatus, "DAQ\_DB\_Transfer",m\_SuppressErrors); if(Message != -1) PostMessage(hwnd,WM\_COMMAND,Message,NULL); return true; } return false;
}
and is sending a custom message defined in the header file.
#define MSG_DRAW_SPECTRUM (WM_APP + 5)
This custom messages refers to member functionOnDrawSpectrum()
where the following code exists to update the window.void CDlg_SpectrumAnalyzer::OnDrawSpectrum()
{int starti,finishi; starti = m\_datacount \* m\_BuffSize / 2; finishi = (m\_datacount + 1)\* m\_BuffSize / 2; if(finishi > m\_AcqNPts) finishi = m\_AcqNPts; int j = 0; for(int i = starti; i < finishi; i++) { m\_fftdata\[i\].re = m\_halfdata\[j++\]; m\_fftdata\[i\].im = 0.; } m\_datacount++; // if we are done collecting draw the graph if((m\_datacount == m\_NBuffs)) { OnSpectrumStart(); float freq = 0; float stepsize = ((float)(m\_MaxFreq)) / ((float)(m\_AcqNPts-1)/2.0); m\_datacount = 0; m\_Graph->ClearGraph(m\_graphNum,false); // do fft fftw\_one(m\_the\_plan, m\_fftdata, NULL); //m\_AcqNPts should be 2^n therefore /2 should be integer for(int i = m\_AcqNPts-1; i >= m\_AcqNPts/2;
AndrewG1231 wrote:
So, my question...in order to get this sequence to run several times, say 5 times, and update the window how would I use
PostMessage()
? Or if I have misunderstood something please let me know.See here for more.
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Show me a community that obeys the Ten Commandments and I'll show you a less crowded prison system." - Anonymous
-
That's a good question... I do believe the framework allows you to do it though... although now that you're questioning it, you're making me question whether I remember that correctly.
You know, some days I don't remember where my head is. Of course you can do it and, in fact, I do it all the time in certain apps. For example, if I have a "DO IT!" button, that usually creates a worker thread that does some stuff and updates a "log view" edit control in the main dialog. The GUI thread continues on and watches for the user clicking on "STOP DOING IT!". Meanwhile, the worker thread does a
TheDialog->m_editcontrol.SetText(logbuffer);
or something equivalent to update the subclassed edit window. Works just fine. I guess I forget this because the "SendMessage()" is buried in the SetText() function and not something I explicitly do but it is still "SendMessage()" from a thread that did not "own" or "create" the control. Oh well, must be getting old.
-
You know, some days I don't remember where my head is. Of course you can do it and, in fact, I do it all the time in certain apps. For example, if I have a "DO IT!" button, that usually creates a worker thread that does some stuff and updates a "log view" edit control in the main dialog. The GUI thread continues on and watches for the user clicking on "STOP DOING IT!". Meanwhile, the worker thread does a
TheDialog->m_editcontrol.SetText(logbuffer);
or something equivalent to update the subclassed edit window. Works just fine. I guess I forget this because the "SendMessage()" is buried in the SetText() function and not something I explicitly do but it is still "SendMessage()" from a thread that did not "own" or "create" the control. Oh well, must be getting old.
Chuck O'Toole wrote:
Oh well, must be getting old.
Happens... Yeah, as a rule of thumb though, I like to tell people to
PostMessage()
when doing things between threads (since they're supposed to be independent anyway). As you probably know, the synchronicity ofSendMessage()
has the potential for deadlocks so it should only be used when you truly need it. -
AndrewG1231 wrote:
So, my question...in order to get this sequence to run several times, say 5 times, and update the window how would I use
PostMessage()
? Or if I have misunderstood something please let me know.See here for more.
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Show me a community that obeys the Ten Commandments and I'll show you a less crowded prison system." - Anonymous
Thanks for the link to the article and I have tried to implement the
PostMessage()
in my code. However, I am still having problems trying to get it to execute more than once and update the window (looping the code is not working). After reading your suggested article, I am still not sure how to do this correctly, can you help? My code is as follows.void CDlg_SpectrumAnalyzer::OnBtnClickedRun()
{
PostMessage(WM_COMMAND,IDC_SPECTRUM_START,0);
}Yes, this is a "Run" button that sends the message and it executes correctly if called only a single time.
-
Thanks for the link to the article and I have tried to implement the
PostMessage()
in my code. However, I am still having problems trying to get it to execute more than once and update the window (looping the code is not working). After reading your suggested article, I am still not sure how to do this correctly, can you help? My code is as follows.void CDlg_SpectrumAnalyzer::OnBtnClickedRun()
{
PostMessage(WM_COMMAND,IDC_SPECTRUM_START,0);
}Yes, this is a "Run" button that sends the message and it executes correctly if called only a single time.
AndrewG1231 wrote:
...it executes correctly if called only a single time.
What happens when the handler is called a second time?
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Show me a community that obeys the Ten Commandments and I'll show you a less crowded prison system." - Anonymous