|
I am just guessing here, based on my own challenges with multi-thread, but perhaps it will help you (?)
The multi thread functions must be static, and there are restrictions on the kinds of access static functons can have. I am guessing m_h0 is not static and therefore not directly acessible by the static multi thread func.
Basically a static function (and static variables) only exists once in memory, regardless of the number of times you instantiate an object from a class. This means a static function cannot know about the specific instance of an object and ita members (at least not directly.
I.e. if class CTest has member NotStatic, and static member Static, then
CTest T1;
CTest T2;
T1.NotStatic = 1;
T2.NotStatic = 2;
ASSERT( T1.NotStatic != T2.NotStatic ); //ok, no conflict
T1.Static = 1; //Static now 1
T2.Static = 2; //Static of ALL CTests now 2!!!
ASSERT( T1.Static != T2.Static ); //FAILS - CTest.Static all the same veriable.
A static function in CTest using NonStatic, would not directly know which instance (T1 or T2) to use, and therefore fails.
However, if you somehow pass the static function enough info to resolve the referencing (usually by a this pointer), you are ok.
Therefore you might try referencing m_h0 using the _this pointer - as far as I can see the THREADSTRUCT includes a _this pointer, which would let the static function access the specific instance of CCash3mfcDlg which holds the specific instance of m_h0. I am guessing the m_ball1 is also non static.
//m_ball1.SetIcon(m_h0); changed to use this pointer
ts->_this->m_ball1.SetIcon(ts->_this->m_h0);
Nitpicking: Why set the icon in a thread, rather than before calling thread in an initialize function? If you have to do it you should probably have a function
CCash3mfcDlg::SetIcon() {
m_ball1.SetIcon(m_h0);
}
and call it from your thread like this
ts->_this->SetIcon();
PS If you are just learning C++, you might want to start easier than multi threading , but good luck, I admire your courage.
|
|
|
|
|
Thanks for your help and encouragement on the project. I actually completed it. You were right about the static pass in the thread. here is what i did.
And as for the setting of the icon I did that for a specific reason. Maybe not the most suffient at the time but I am still learning. I have 3 icons on the screen to represent balls from 0 to 9 and i randomly generate a number sequence consisting of three numbers while the thread runs. and it displays that number sequence represented as balls. and I run it in the thread so it makes the balls look as if they are spinning.
here is a portion of the source pertaining to the thread.
http://rafb.net/paste/results/rx590089.html
Thanks again,
Win32newb
"Making windows programs worse than they already are"
|
|
|
|
|
Glad I could help
BTW, the link http://rafb.net/paste/results/rx590089.html does not seem to work
As for my nitpick about setting icons, please ignore that.
If you are trying to do sprite like animation (using icons because they can have transparency) running the "frames" from a worker thread seems like a good solution (IMO).
Jens
|
|
|
|
|
It was my understanding that you should not pass pointers to CWnd objects like dialogs into threads, but pass the handle (using GetSafeHwnd), and then retrieve pointer in thread using CWnd* pWnd = CWnd::FromHandle(pParam).
But, I might be wrong - I am here becuse the examples showing the above does not seem to work for me when trying to give me access to a dialog from within a thread
(the returned pointer seems to point to some other uninitialized instance)
If anyone knows about this I would love to hear from you....please?
Example: CMyDlg:CDialog has member variable int m_iX = 5424992;
CMyDlg::OnBtnGo{) {
...
AfxBeginThread(MyProcc, this->AfxGetSafeHwnd())
...
}
UINT MyProcc(LPVOID pParam) {
CMyDlg* pDlg = (CMyDlg*)(CWnd::FromHandlePermanent((HWND)pParam));
TRACE1("X=%i", pDlg->m_iX;
}
results in 4258440 not 5424992 ?????
Incidentally, pDlg != this pointer in dlg,
but pParam = m_hWnd of dialog
Arrgghhhhh!!!
Any help on HOW I can access a member in MuDlg from a thread would be appreciated.
Jens
|
|
|
|
|
Correction: AfxBeginThread(MyProcc, this->GetSafeHwnd()) NOT AfxBeginThread(MyProcc, this->AfxGetSafeHwnd())
Sorry about that
|
|
|
|
|
As others have noted, when using threads one might get an assertion if passing objects by pointers. Specifically in Wincore.cpp line 886:
// Note: if either of the above asserts fire and you are
// writing a multithreaded application, it is likely that
// you have passed a C++ object from one thread to another
// and have used that object in a way that was not intended.
// (only simple inline wrapper functions should be used)
//
// In general, CWnd objects should be passed by HWND from
// one thread to another. The receiving thread can wrap
// the HWND with a CWnd object by using CWnd::FromHandle.
//
// It is dangerous to pass C++ objects from one thread to
// another, unless the objects are designed to be used in
// such a manner.
I still have no sucess passing access to my dialog using the handle - I seem to get a pointer to something uninitialized.
However, I did have better success acessing things in the dialog when I stopped using the handle, and returned to using the this pointer AND allocated the member objects I want on the heap using "new" command (or is that the stack? I don't remember right now) during my dialogs initialization.
I have no idea if this is "safe", or just avoids the MFC asserts letting me run the code. It seems to work though, but if anyone knows if this is the "proper" and safe way to do it, please post!
Anyway, if you have problems getting pointers to stuff into your threads, try allocating them with new.
I admit that by know I am completely confused
I really had the impression that objects to threads should be passed as handles, but...
Jens
|
|
|
|
|
Something on Multithread programming in VC 6 online help under Multithreading: Programming Tips
Accessing Objects from Multiple Threads
For size and performance reasons, MFC objects are not thread-safe at the object level, only at the class level. This means that you can have two separate threads manipulating two different CString objects, but not two threads manipulating the same CString object. If you absolutely must have multiple threads manipulating the same object, protect such access with appropriate Win32 synchronization mechanisms, such as critical sections. For more information on critical sections and other related objects, see Synchronization in the Win32 SDK.
The class library uses critical sections internally to protect global data structures, such as those used by the debug memory allocation.
Basically, CWnd objects only exist in the threads that they are created in, so passing window classes by handle and casting them does not cause the variables you defined in the derived window classes to be passed over, you are actually creating a new object pointing to the same window handle.
For me, I usually encapsulate my data members in a separate object/structure and pass the pointer to that object/structure.
The alternative is to pass the window classes by pointer and use mutexes to coordinate the access to the data members. It is more risky though because the OS itself is also access your window pointer to perform management functionality such as redraw etc.
|
|
|
|
|
"...you are actually creating a new object pointing to the same window handle"
That was what I assumed was happening, thanks for confirming that.
What I ended up doing was using a Functor. Basically a simple template class that takes a pointer to an object and a pointer to a function in the object, and then has a function call to execute using the pointers.
The Function Pointer site has some excellent explanation of function pointers, Functors, etc. I highly recommend it:
http://www.function-pointer.org/index.html
This aproach allows me to pass the Functor to a static UINT ThreadRun(pFunctor) function I can call from AfxBeginThread, and ThreadRun then uses Functor to run the function I really want to run, in the object I want to run it in (obviously with critical sections as appropriate for multi-thread).
As far as I can tell (but I am just learning this), the Functor class is the "approved" approach to accomplish what the author of this article tried with his structure, but being a template class it is more generic, and having a destructor prevents memory leaks if used correctly)
This has been quite a learning experirnce for me - perhaps this thread will help the next programmer who is trying this.
Jens Winslow
|
|
|
|
|
... great idea
Maybe I will update the article in the near future.
Enis Arif
-----------
"I am enough of an artist to draw freely upon my imagination. Imagination is more important than knowledge. Knowledge is limited. Imagination encircles the world." (Albert Einstein)
|
|
|
|
|
Thanks, but I really can't take much credit. I just did as Lars Haendel suggested on his site.
Jens Winslow
|
|
|
|
|
It occurred to me that people might wonder why I choose the more complicated use of a Functor instead of just using a structure with a this pointer and data parameters (now that I am done, I wonder too).
Let me share with you a tale of mystery and wonder - a tale of a painful journey!
First I did try passing a pointer to a structure...
In my first structure based approach I defined a structure and enumerator to pass to a static UINT Command_T( LPVOID pCmdFunctor) which I could call from AfxBeginThread.
<br />
enum EChanCmd {<br />
INITIALIZE,<br />
SERVERCONNECT,<br />
CLIENTCONNECT,<br />
SEND,<br />
RECEIVE,<br />
CANCEL<br />
};<br />
<br />
struct SChanCmd {<br />
CMuxChannel* pChan;<br />
SMuxChannelEventData* pParam;<br />
EChanCmd eCmd;<br />
}<br />
Command_T is a switch statement that then calls the appropriate command function in the CMuxChannel object pointed to by pChan. For debugging I used a TRACE before and after the call, calling pChan->GetStatus(), a member function created to tell me the status .
At first things seemed to work fine . It was a bit of a pain to declare SChanCmd every time I wanted to issue a command with a worker thread, making for slightly complicated code like:
<br />
SChanCmd uCmd;<br />
uCmd.pParam = new SMuxChannelEventData;
uCmd.pParam->wp = (WPARAM)(USHORT)m_uiPortNum;<br />
uCmd.pParam->lp = (LPARAM)(LPCTSTR)m_strIPAddr;<br />
uCmd.eCmd = CMuxChannel::CLIENTCONNECT;
uCmd.pChan = &m_Chan;<br />
AfxBeginThread(CMuxChannel::Command_T, &uCmd);<br />
Add to this a large ugly switch statement in Command_T, and my code was esteticaly not to pleasing. But after 3 days of battling with it I did not care!
Then I hit a minor snag.
Some of my CMuxChannel commands might throw an exception (which are appropriately handled in the class). To my surprise, if an exception occurs my uCmd->pChan is set to NULL!!!! God only knows what else may be happening! I am guessing that the unwinding of the heap / stack occurring during a throw/catch exception somehow blows away my pointer (I tried saving a backup and using it, but that did not work).
What ever is happening made me very nervous about the condition of m_Chan, and my ability to call functions from AfxBeginThread.
I then tried using the Functor template (see link above), but I was not quite able to get it working, so I created my own less generic functor specifically for CMuxChannel. The concept was simple:
A class with a pointer to an instance of CMuxChannel, a function pointer to a command function in CMuxChannel, and a pointer to the parameter. Add a Call function to do the call, and a constructor taking a pointer to CMuxChannel and the desired function, and BINGO!
Now I can call my function like this, which looks a lot better:
<br />
CMuxChannel::CCmdFunctor uCmd(&m_Chan, CMuxChannel::ConnectAsClient);<br />
AfxBeginThread(CMuxChannel::Command_T, &uCmd);<br />
In reality the syntax for my Functor was murder to get right (had I known I would not have bothered, and just wrapped each command function in a static UINT AfxBeginThread function) But I did get it working at last.
<br />
class CCmdFunctor {<br />
public:<br />
CCmdFunctor(CMuxChannel* pChan, void (CMuxChannel::*ptrCmdFunc)(SMuxChannelEventData*), SMuxChannelEventData* pParam = NULL);<br />
<br />
void Call() { (*m_pChan.*m_ptrCmdFunc)(m_pParam);}<br />
<br />
CMuxChannel* m_pChan;
private:<br />
<br />
void (CMuxChannel::*m_ptrCmdFunc)(SMuxChannelEventData*);<br />
SMuxChannelEventData* m_pParam;<br />
};<br />
<br />
static UINT Command_T( LPVOID pCmdFunctor);
In conclusion:
1) Functors, multi-threaded, and function pointers are not for the faint of heart!
2) Something strange happens to your pointers to objects if the object uses / throws exceptions internally (feel free to explain this if you can)
3) Why did I ever want to become a programmer anyway?
Perhaps this will help someone else (I hope)
Me, I am thinking about becoming a farmer
Perhaps this was not a good day to code after all;P
Jens Winslow
|
|
|
|
|
I had a rather nasty surprice...
Since the functor is called by AfxBeginThread, we must be carefull that Functor does not go out of scope before AfxBeginThread has called the Call function, or nasty things happen, like access violations and other randomness
My solution: create Functor on heap with new
pCmd = new CMuxChannel::CCmdFunctor(&m_Chan, CMuxChannel::ConnectAsServer);
and have my Command_T function responsible for deleting it, when thread exits and we presumably are done with info in Functor)
UINT CMuxChannel::Command_T( LPVOID pCmdFunctor )<br />
{<br />
CCmdFunctor* p = (CCmdFunctor*)pCmdFunctor;<br />
CString str = p->GetChan()->GetState();<br />
p->Call();<br />
delete p;
str = str + " -> " + p->GetChan()->GetState();<br />
TRACE1("\nSM: %s ",str); <br />
return 0;<br />
}
Jens Winslow
|
|
|
|
|
|
WOW... this the the thread I've been looking for for quite some time... I was having so much trouble getting access to my dialog classes controlls from within my worker thread.. I'm gonna see if I can code this example of the functor approach in, from the looks of it, I think this is gonna work out... Thank You
|
|
|
|
|
I also have the same problem
|
|
|
|
|
tc->_this->UpdateData( TRUE ) gets an assertion failure. Why is this?
|
|
|
|
|
You should not get an assertion failure. I tried it and it works fine. Maybe your dialog isn'n created when you call UpdateDate. Do you have more details?
Enis Arif
-----------
"I am enough of an artist to draw freely upon my imagination. Imagination is more important than knowledge. Knowledge is limited. Imagination encircles the world." (Albert Einstein)
|
|
|
|
|
I re-downloaded your example (i.e. started from scratch) and made the while loop in the StartThread function look like this:
while (ts->_this->m_ctrl_progress.GetPos () < 1000)
{
Sleep(500);
ts->_this->m_ctrl_progress.StepIt ();
ts->_this->UpdateData( TRUE );
}
Running the program (in debug mode) gives an assertion failure. Underneath the assertion failure is the message (in wincore.cpp):
// Note: if either of the above asserts fire and you are
// writing a multithreaded application, it is likely that
// you have passed a C++ object from one thread to another
// and have used that object in a way that was not intended.
// (only simple inline wrapper functions should be used)
//
// In general, CWnd objects should be passed by HWND from
// one thread to another. The receiving thread can wrap
// the HWND with a CWnd object by using CWnd::FromHandle.
//
// It is dangerous to pass C++ objects from one thread to
// another, unless the objects are designed to be used in
// such a manner.
Doesn't the ts->_this-> portion take care of the window handle?
|
|
|
|
|
I tried this in VC6, and it does not seem to work, no update occurs.
Jens
|
|
|
|
|
I also have the same problem. I'm trying to insert text into an edit control and UpdateData via the "ts" pointer and I get an assertion failure. Has anyone come up with a solution yet?
|
|
|
|
|
Assert error is a must for high VC liks VC7.
CDialog is base CWnd, it has own thread.
Now you access CDialog member function in other working thread.
If the member function allows you to read things. It is OK.
But if it changes things or affect the CDialog variables which are under the scope of its own thread. Then you will get coredump, memory leak or assertion error (depends on your compiler and debugger settings)
Also, MFC class is not thread safe.
Thread to MFC Thread.
The easy way is to send message.
Let the MFC Thread to handle the changes.
Or you have to create critical section and comparment which are not easy to made.
The concepts and the mistakes here are indeed quite common mistake for those who understands MFC clearly.
A reference book : Visual C++.Net Bible - Chapter for thread
|
|
|
|
|
I am new to MFC, just trying out with some self-projects related to file transfer using serial communication. when I tried using Threads, I have same problem with "UpdataData". I referred the above posted code. So is there any solution in VC++ 6.0?
Pls. let me know.
|
|
|
|
|
To solve this problem with calling UpdateData(); you must create CWnd* pionter using methids this->FromHandle(this->m_phWnd); then convert pointer to you window class . It happen becuse you must get window objet nit a pinter to class associatet with window
Gress
|
|
|
|
|
You should not be using UpdateData() , let alone in a secondary thread! To properly update the UI from a secondary thread, the only safe way is to post a message back to the primary thread (the one that owns the UI) indicating what needs to be updated. Read this article for more.
"When I was born I was so surprised that I didn't talk for a year and a half." - Gracie Allen
|
|
|
|
|
Ok.. with all said... What is the correct way to update a dialog from within a worker thread..
I've tried creating a refresh() function conected to the dialogs class.. Works fine from any CDialog:: classes.. but not from within workor thread.. I've tried pointers to the Cdialog function to get access from within the worker thread but UpdateData does not work..
Cstring variable;
CTestDlg * pointer = new CTestDlg
BOOL CMainDlg::OnInitDialog()
{
CDialog::OnInitDialog();
AfxBeginThread(workThread ,NULL);
return TRUE;
}
void CTestDlg::Refresh() //different dialog box
{
UpdateData(TRUE);
m_Listbox.SetWindowTextW(variable);
}
UINT workThread()
{
while(programisrunning){
variable = "text"; //<-- changeing variable throughout program actually..
pointer.Refresh();
}
}
This is pretty much the setup I need in my program.. I just can't get things workin no matter what I try...
Any Ideas.. ??
-- modified at 11:50 Monday 23rd October, 2006
|
|
|
|
|