|
I wrote a simple MFC multithreaded program using mutexes that was based on a sample MFC multithreaded program I found somewhere. I want to get time in the worker thread and then display it in the main dialog. I tried to pass the data between the threads using using global variables declared in a .h file included in both modules but I keep getting a link error, i.e., TimeThread.obj : error LNK2005: "long icount" (?icount@@3JA) already defined in clockDlg.obj. I am sure that there is a better way to pass data between threads than this way, anyway. I would appreciated some help as to the best way to pass data between threads.
Greg
|
|
|
|
|
Your linker problem is caused by the fact that the global variable "icount" is instantiated in every source file that includes the .h that declares it.
Try:
(In .h file)
<ref>
...
extern volatile long icount;
..
and in either of TimeThread.cpp or clockDlg.cpp (but not both)
<ref>
...
volatile long icount;
...
Without the "volatile" keyword, it doesn't matter how many mutexes you use, different threads may see different values depending on compiler optimisations.
It doesn't look to me as though you really need synchronisation with what you're doing here though, so why not just use RegisterWindowMessage(...) at startup to get a unique message ID & use PostMessage(...) from your worker thread to send the value to your dialog?
Regards,
T-Mac-Oz
|
|
|
|
|
Hi
I want to learn if i there is a list of defining .cpp files that mfc functions are defined.
I assume all the source code is in that directory C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\src\mfc.
For example in that code:
ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew)
I want to see CWinApp::OnFileNew's implementation.
I tried to put breakpoint in that line strat to debug by F11. But i can not find the source code.
Is there another solution or where am i wrong?
For an example; How can i find the CWinApp::OnFileNew's implementation file?
I am looking for your answers.
Thanks.
|
|
|
|
|
I spend a significant amount of time looking into the ATL/MFC source code.
Thankfully, the Find in Files dialog saves used paths!
Ctrl-Shift-F, select the path in the dropdown (project, solution, PSDK includes, CRT source,
ATL/MFC source are the ones I use all the time), type in the Find What edit box if it's not there
already, and go.
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
You could try adding an ID_FILE_NEW handler to your application class which calls the base class implementation & setting a breakpoint on it.
i.e.
<ref>
void C{YourProject}App::OnFileNew()
{
CWinApp::OnFileNew(); // Set breakpoint here
}
Regards,
T-Mac-Oz
|
|
|
|
|
Thank you
Both of your solutions work well.
Good works...
|
|
|
|
|
Have you tried F12?
"A good athlete is the result of a good and worthy opponent." - David Crow
"To have a respect for ourselves guides our morals; to have deference for others governs our manners." - Laurence Sterne
|
|
|
|
|
Thanks for the handy tip-of-the-day!
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
All of a sudden I cannot debug my applications. When I go to start, I get the above error.
I'm running VS2005SP1 with unmanged c++
I was able to run the other day, but today i cannot. This is code I'm trying to run (pretty simple)
--------------------------------------------------------
int main(int argc, char *argv[])
{
return 0;
}
---------------------------------------------------------------
Any thoughts
Tom
|
|
|
|
|
I don't know if it helps any, but a google search on "msdia80.dll"
yields several fixes for assorted problems related to that DLL.
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
I did look there first. However, none of them matched my situation close enough. I'm trying not to have to re-install vs2005 to fix problem.
|
|
|
|
|
I did a repair install of VS2005 (1.5 hours), and that fixed the problem.
It appears some setting somewhere was corrupted.
Tom
|
|
|
|
|
Bummer.
Thanks for the update though...good to know!
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
i created a mfc dialog with worker thread, i used events for communication between main thread and worker thread, but i ran into some problems i can't figure it out.
UINT ThreadFunc(LPVOID pParam)
{
THREADPARMS*ptp=(THREADPARMS*)pParam;
CWnd*pWnd=ptp->pWnd;
CMy61xxMTDlg*pDlg=ptp->pDlg;
CEvent* pKillEvent=ptp->pKillEvent;
CEvent* pOutputEvent=ptp->pOutputEvent;//ptp->pOutputEvent=&m_OutputEvent
delete ptp;
while(::WaitForSingleObject(pOutputEvent->m_hObject,1000)==WAIT_OBJECT_0)
{
pDlg->m_OutputEvent.ResetEvent();
SpcGetParam(//output some data, an external library function)
pWnd->PostMessageW(WM_USER_GET_DATA,0,0);
// i need to access GUI object-sliders, and store the positions of sliders into array, so i post a message,the handler is OnGetData()
if(::WaitForSingleObject(pKillEvent->m_hObject,0)==WAIT_OBJECT_0)
{
pWnd->PostMessageW(WM_USER_THREAD_ABORTED);
return -1;
}
return 0;
}
LRESULT CMy61xxMTDlg::OnGetData(WPARAM wParam, LPARAM lParam){
CMy61xxMTDlg*pDlg=(CMy61xxMTDlg *) GetActiveWindow();
if(pDlg!=NULL)
pDlg->GetData(pbyData[nBufIdx], BUFSIZE);
return 0;
}
void CMy61xxMTDlg::GetData(ptr8 pbyData, int32 lBufsize)
{
int i;
for (i=0; i<BUFSIZE; i ++)
{
pbyData[i]=(int8)m_Slider_Data[i].GetPos();
}
m_OutputEvent.SetEvent();
}
i set the m_OutputEvent to be manual reset, the thread wait for the signal
<pre>while(::WaitForSingleObject(pOutputEvent->m_hObject,1000)==WAIT_OBJECT_0);<pre>
then i reset the signal
<pre>pDlg->m_OutputEvent.ResetEvent();<pre>
then i post a message to run the CMy61xxMTDlg::GetData() function, which stores the slider position in the data array, at the end of the function, i signal the outputevent
<pre>m_OutputEvent.SetEvent();<pre>
the idea is that i don't want the data to be output(SpcGetParam() function) if the data is not ready yet.
i found that the loop
<pre>while(::WaitForSingleObject(pOutputEvent->m_hObject,1000)==WAIT_OBJECT_0) <pre>
only run for once, and second time the it waits(actually the program freeze a little bit) for SEVERAL seconds and jumped to "return 0" statement, which means the m_OutputEvent is not signaled,, this is quite weird....
what's happening?
-- modified at 16:56 Friday 10th August, 2007
|
|
|
|
|
It's hard to tell what's happening - you left out some brackets somewhere in your posted code.
What's the purpose of the second thread? Just to do something every (approx) 1000ms?
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
the a worker thread is to continuously output the position of sliders(mentioned in my previous posts),
the worker thread waits for the signal that data is ready for output, and waits for 1000ms, should take that long to complete the GetData() function, in my case, only 4 sliders.
|
|
|
|
|
As far as I can tell from what you've posted, the extra thread is unnecessary.
Any time you need to pick an arbitrary timeout that "should" be the right amount of time,
there's probably something flawed in the design.
I can't tell what's going on since there's missing code (at least one curly bracket is
missing from the thread proc).
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
since i need to continuous output the position of sliders while i can STILL change the drag the sliders, i definitedly need a worker thread.
the basic idea is that i have a dialog with 4 sliders, a start button and a stop button. when start button clicked, a worker threads starts output the current position of the sliders, and stop when stop button clicked.
here's a more complete code:
in CMy61xxMTDlg.h:
//define user message
#define WM_USER_GET_DATA WM_USER+0x100
#define WM_USER_THREAD_ABORTED WM_USER+0x101
//define thread function
UINT ThreadFunc(LPVOID pParam);
//define thread function parameter
typedef struct tagTHREADPARMS {
CWnd* pWnd;
CEvent* pKillEvent;
CEvent* pOutputEvent;
class CMy61xxMTDlg* pDlg;
} THREADPARMS;
class CMy61xxMTDlg : public CDialog
{.....
afx_msg LRESULT OnGetData(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnThreadAborted(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
.....
HANDLE m_hThread;
CEvent m_KillEvent;
CEvent m_OutputEvent;
void GetData(char* pbyData, int32 lBufsize);
void ThreadAborted();
......
}
in CMy61xxMTDlg.cpp:
BEGIN_MESSAGE_MAP(CMy61xxMTDlg, CDialog)
......
//user defined message handler
ON_MESSAGE (WM_USER_GET_DATA, OnGetData)
ON_MESSAGE (WM_USER_THREAD_ABORTED, OnThreadAborted)
END_MESSAGE_MAP()
void CMy61xxMTDlg::OnBnClickedButtonStart()
{
// TODO: Add your control notification handler code here
THREADPARMS*ptp=new THREADPARMS;
ptp->pDlg=this;
ptp->pWnd=AfxGetMainWnd();
ptp->pKillEvent=&m_KillEvent;
ptp->pOutputEvent=&m_OutputEvent;
CWinThread* pThread=AfxBeginThread( ThreadFunc,ptp,-1,0,CREATE_SUSPENDED);
::DuplicateHandle(.....)
m_OutputEvent.SetEvent();
pThread->ResumeThread();
}
void CMy61xxMTDlg::OnBnClickedButtonStop()
{
// TODO: Add your control notification handler code here
//Stop thread
m_KillEvent.SetEvent();
}
UINT ThreadFunc(LPVOID pParam)
{
THREADPARMS*ptp=(THREADPARMS*)pParam;
CWnd*pWnd=ptp->pWnd;
CMy61xxMTDlg*pDlg=ptp->pDlg;
CEvent* pKillEvent=ptp->pKillEvent;
CEvent* pOutputEvent=ptp->pOutputEvent;
delete ptp;
pWnd->PostMessageW(WM_USER_GET_DATA,0,0);
while(::WaitForSingleObject(pOutputEvent->m_hObject,0)==WAIT_OBJECT_0 && pDlg->nErr==ERR_OK)
{
pDlg->m_OutputEvent.ResetEvent();
SpcSetParam (pDlg->hDrv, SPC_FIFO_BUFREADY, pDlg-> nBufIdx);//some external library function, transfer data to a hardware and output
pWnd->PostMessageW(WM_USER_GET_DATA,0,0);
if(::WaitForSingleObject(pKillEvent->m_hObject,0)==WAIT_OBJECT_0)
{
pWnd->PostMessageW(WM_USER_THREAD_ABORTED);
return -1;
}
}
return 0;
}
LRESULT CMy61xxMTDlg::OnGetData(WPARAM wParam, LPARAM lParam){
CMy61xxMTDlg*pDlg=(CMy61xxMTDlg *) GetActiveWindow();
if(pDlg!=NULL)
pDlg->GetData(pbyData[nBufIdx], BUFSIZE);
return 0;
}
LRESULT CMy61xxMTDlg::OnThreadAborted(WPARAM wParam, LPARAM lParam){
CMy61xxMTDlg*pDlg=(CMy61xxMTDlg *) GetActiveWindow();
if(pDlg!=NULL)
pDlg->ThreadAborted();
return 0;
}
void CMy61xxMTDlg::GetData(ptr8 pbyData, int32 lBufsize)
{
int i;
for (i=0; i<BUFSIZE; i ++)
{
pbyData[i]=(int8)m_Slider_Data[i].GetPos();
}
m_OutputEvent.SetEvent();
}
hope the code is clear this time, the problem is that the while loop in the thread function only run once....
-- modified at 17:08 Friday 10th August, 2007
|
|
|
|
|
There's still a missing curly brace in ThreadFunc
You're showing a while loop with the closing brace after the return 0.
What's the reason for the thread? Does SpcSetParam take a long time to output?
As it is right now, you're just looping in the thread, outputting values as fast as you can get them.
And you're using the UI thread to get those values
This will hurt your UI more than not using a thread!
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
yes, i am using the UI thread to get the position of sliders in UI thread, because i can't do that in the worker thread directly.
the while loop is waiting for the m_OutputEvent to be signaled, once signaled, and then immediately the event is reset in the loop, so next time, the while loop is blocked until is event is signaled again.
in the while loop i post a message to run GetData() function to get slider positions from UI thread,once this done, i signaled m_OutputEvent so the while loop is unblocked.
so the basic mechanism is:
data ready-> signal m_OutputEvent, unblock while loop->reset m_outputEvent->run SpcSetParam() function to output data->post message->run GetData() function->data ready-> signal m_OutputEvent..... and goes on
i did this to prevent that the SpcGetParam run before the data is ready.
|
|
|
|
|
Right, got all that. You're avoiding my questions though
Worker thread waits on the event
(Assuming the event is set within 1000ms the first time around...)
Worker thread resets event
Worker thread calls SpcSetParam
Worker thread posts the message.
UI thread gets the message, gets the data, sets the event.
Loop
Unless SpcSetParam takes alot of time, you're posting alot of events to the ui thread per second.
If SpcSetParam needs continuous input, regardless of changes to the sliders, that's one thing.
If SpcSetParam doesn't need to be called unless a slider position changes, then it would be WAY
more efficient to make it event driven - just call SpcSetParam whenever a slider changes.
Again, as it looks now, you're getting little-to-no benefit from the extra thread.
Mark
Mark Salsbery
Microsoft MVP - Visual C++
|
|
|
|
|
ok, the hardware i'm using is a analog signal generator, and i monitor the signal on the oscilloscope.
the sliders are used to change the signal output.
the hardware come with its own library functions, which is the SpcSetParam().it sets the hardware to run in FIFO mode, a typical mode for continuous data output.
the SpcSetParam get the data from a software buffer, in this case, my data array contain slider positions,pass it to hardware buffer, then the data in hardware buffer is outputed and i can see it on the oscilloscope.
i want to see the output continously, it can't just run it once and gone, that's why i'm looping.
SpcGetParam() does not take long since the hardware processing speed is 100MB/s.
SpcGetParam() just take the data buffer contain the slider position, so the output doesn't reflect on immediate change of sliders, since looping is fast, the effect is almost immediate.
|
|
|
|
|
I agree with Mark, event driven is definitely the way to go.
Option 1:
declare pbyData[] as an array of volatile values
use OnHScroll & OnVScroll to update the pbyData[] values when slider positions change
Add a pointer value (assigned to pbyData) as one of the members of THREADPARMS
Then you only have to check for the kill event in your worker thread.
Only one event to monitor which is only ever set from the main thread, no deadlocks.
NOTE: Without a SwitchToThread() call in the worker thread loop, this approach will kill your interface responsiveness (& your O/S responsiveness without a multicore CPU!). If you're thinking of a Sleep(...) call, you might as well use a timer...
Option 2:
use OnHScroll & OnVScroll to update the pbyData[] values when slider positions change
Set a timer (delay value depending on exactly what you mean by "continuously output")
Call SpcGetParam() in OnTimer()
No threads, no deadlocks.
T-Mac-Oz
|
|
|
|
|
hi, T-Mac, thx for the reply. I'm not sure if you familiar with the First in,First out(FIFO) mode,the hardware use the fifo mode and the function, SpcGetParam(), i can't change it.
the ScpGetParam function just take a chunk of data in the software data buffer and store the content to onboard hardware buffer, wut if i'm trying to write data into pbyData[] in OnVScroll() while SpcGetParam() try to reads it?
the SpcGetParam() could be running at 100Hz, i really don't think using a timer is such a good idea.
the thing i'm trying to do is like changing the output anytime and the data is outputing contiunously and endlessly, and i can monitor the output on oscilloscope in real time.
i really don't have any better idea other than running a worker thread and using message to tell the main thread to store the data. if someone have any better way to do it, please do tell me
|
|
|
|
|
Hi Albert,
After having another look at your original posted code, it seems you are using threads to perform what is essentially a sequential task!
Worker thread posts WM_USER_GET_DATA
Main thread receives WM_USER_GET_DATA*
Main thread calculates output
Main thread signals "output" event
Worker thread receives "output" event
Worker thread outputs data
Worker thread posts WM_USER_GET_DATA
Main thread receives WM_USER_GET_DATA*
Main thread calculates output
Main thread signal "output" event
Worker thread receives ...
* Yes, the main thread is processing other messages but given that the WM_USER_GET_DATA is sent to the main thread using PostMessage(...), the worker thread ends up having to wait on processing of other messages before its WM_USER_GET_DATA message is processed anyway!
A much cleaner solution that accomplishes exactly the same thing is:
#define WM_USER_GET_DATA WM_USER+0x100
class CMy61xxMTDlg : public CDialog
{
.....
bool m_bDoOutput;
afx_msg LRESULT OnGetData(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnUserStop(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
.....
void OnUserGetData();
void OnBnClickedButtonStart()
{
m_bDoOutput = true;
PostMessage(WM_USER_GET_DATA);
};
void OnBnClickedButtonStop() { m_bDoOutput = false; };
.....
}
......
BEGIN_MESSAGE_MAP(CMy61xxMTDlg, CDialog)
......
ON_MESSAGE (WM_USER_GET_DATA, OnUserGetData)
......
END_MESSAGE_MAP()
void CMy61xxMTDlg::OnUserGetData()
{
if (!m_bDoOutput)
return;
ptr8 pbyData;
for (i=0; i<BUFSIZE; i ++)
{
pbyData[i]=(int8)m_Slider_Data[i].GetPos();
}
SpcSetParam(...);
PostMessage(WM_USER_GET_DATA);
}
Threads are great for their intended purpose, which is to permit semi-independent operations to execute in parallel.
The tight looping and total dependence on event signals should have tipped me off right away that what you were trying to achieve was not really parallel processing at all.
As you've already found out, threads and thread synchronisation are tricky beasts, so it's always worth exploring alternative implementations (such as timers and/or user message events) before resorting to a multi-threaded solution to a problem.
Threading sounds cool & it is cool but don't leap into trying to use threads just because you think you can, you will most often come up with a sub-optimal solution.
BTW. I do understand the FIFO concept & that is precisely why using a timer is a better idea than continually looping in a background thread (or perhaps even the self-propagating event solution above).
If, as you say, SpcGetParam(...) adds the supplied data in the on-board hardware buffer (i.e. the end of the FIFO queue), then a system running at 2Gz (forget FSB, the small volume of data we're talking about here can almost be guaranteed to always be cached), with the other minimal processing involved, feeding a FIFO queue being read at 100MHz will sooner or later (probably sooner) fill & overflow (or maybe wrap around, or block, or drop values, depends on the firmware) the hardware buffer. Using a timer gives you the opportunity to match your output frequency to the oscilloscope frequency. If the standard system timer resolution is not fine-grained enough for you, there are other timers available in windows, search Code Project for "timers" & you'll find plenty of articles. If you insist on using threads, protecting access to m_pbyData[] as a member of CMy61xxMTDlg is easily accomplished using mutexes.
Regards,
T-Mac-Oz
|
|
|
|
|