|
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
|
|
|
|
|
aquawicket wrote: What is the correct way to update a dialog from within a worker thread..
By posting a message to the thread that owns the UI.
"Approved Workmen Are Not Ashamed" - 2 Timothy 2:15
"Judge not by the eye but by the heart." - Native American Proverb
|
|
|
|
|
Ok... Finally figured it out..
You are not suppose to make any calls to the dialog objects while in the worker thread.. Instead.. You must have your worker thread call another thread that will call it through a windows message Example using SendMessage()...
#define UWM_DOSOMETHING WM_APP+30 //define the User Windows Message
BEGIN_MESSAGE_MAP(CRetriggerDlg, CDialog)
ON_MESSAGE(UWM_RETRIGREFRESH, DoSomething) //Link the UWM to a class function
END_MESSAGE_MAP()
BOOL CMainDlg::OnInitDialog()
{
CDialog::OnInitDialog();
AfxBeginThread (WorkerThread, this); //call the worker thread
return TRUE;
}
UINT CMainDlg::WorkerThread( LPVOID pParam )
{
//Do your work in here
CMainDlg * me = (CMainDlg *)pParam;
me->MsgPump();
return 0;
}
void CMainDlg::MsgPump()
{
SendMessage(UWM_DOSOMETHING);
}
LRESULT CMainDlg::DoSomething(WPARAM, LPARAM)
{
//Play with the dialog window as you please here...
}
This snip of code should be enough to get anyone goin on using their working thread to talk to the dialog.. And yes.. UpdateData(); works just fine.. Just keep calls to the dialog outa the workerthread and the msgpump functions.. let DoSomething function do the talking..
-- modified at 19:19 Tuesday 24th October, 2006
|
|
|
|
|
How can I terminate this type of thread early?
|
|
|
|
|
Normally, you can call AfxEndThread() in the controlling function.
The MSDN documentation says: Call this function [AfxEndThread] to terminate the currently executing thread. Must be called from within the thread to be terminated. I hope this helps.
Enis Arif
|
|
|
|
|
Thanks for your help. In my case, I have I very long process working on my
worker thread, and I need to terminated the thread from the calling module.
Any idea how to do this ?
Thanks
|
|
|
|
|
You have to call AfxEndThread from the worker thread. If you have let's say a Stop button on your calling module, you can set a bool member var in your OnStop() method and check this var in your thread. If it is true then call AfxEndThread().
This should solve the problem, but be carefull what happens with the data your thread has already modified.
Enis Arif
|
|
|
|
|
|
//In .h of the class
///////////////////////
class CToto
{
// Implementation
protected:
struct SToto
{
BOOL m_bToto;
};
static UINT ThreadToto(LPVOID pParam);
};
//In .cpp of the class
////////////////////////
CToto::CToto()
{
SToto stToto;
SToto.m_bToto = TRUE;
AfxBeginThread(CToto::ThreadToto, this);
}
UINT CToto::ThreadToto(LPVOID pParam)
{
CToto* pToto = (CToto*)pParam;
pToto->SToto.m_bToto = FALSE;
return 0;
}
Enjoy!!!;)
|
|
|
|
|
|
Indeed there is no delete. There are two situations here, and I'm not sure yet which one is better:
1) you can destroy the pointer in the worker thread
2) you can destroy the pointer in the calling function after the thread exits
In both situations, I don't really now what happens with thread safety. Actually, I'm trying to get this now. This was just an example of how to create such a worker thread, because MS doesn't say a word about it.
Enis Arif
|
|
|
|
|
Enis wrote:
you can destroy the pointer in the worker thread
That's fine, though if you had passed the class instance pointer, deletion would have been unnecessary!
Enis wrote:
you can destroy the pointer in the calling function after the thread exits
This would mean waiting for the thread to finish, in which case a stack variable of local scope would actually suffice!
Nish
Author of the romantic comedy
Summer Love and Some more Cricket [New Win]
|
|
|
|
|
The stack variable is out of the question... you will get in very nasty problems when you have multiple threads. Passing the class instance pointer makes the deletion unnecessary (so I though, I don't know if I was wrong...though I didn't get a memory leak in my tests)
Enis Arif
|
|
|
|
|
Enis wrote:
The stack variable is out of the question... you will get in very nasty problems when you have multiple threads
That's a very confusing statement! If the stack variable is in scope as long as the thread executes, it won't cause any problems at all. Of course it's a bad idea to do that, but it won't cause any thread issues. Since in your particular case the stack variable is simply s struct with one member that points to a class instance.
Enis wrote:
Passing the class instance pointer makes the deletion unnecessary
Yes, since the class gets destructed when it goes out of scope, usually when the program exits or when the window is closed, if it is a window class like say CWnd.
Author of the romantic comedy
Summer Love and Some more Cricket [New Win]
|
|
|
|
|
IF your instantiating func intends to wait around for any threads it starts to terminate, then its actually a better idea to pass on the stack than on the heap (i.e. with new/delete).
BTW, passing the class instance pointer doesn't always protect you from stack issues. Consider this code:
void Foo()
{
MYCLASS MyClass;
MyClass.DoBackgroundProcess();
return;
}
void MYCLASS::DoBackgroundProcess()
{
AfxBeginThread(StartThread, (PVOID)this)
}
// worker thread func
UINT MYCLASS::StartThread(PVOID pThis )
{
(MYCLASS*)pThis->m_membervar++;
}
Any heap or stack based object passed to a worker thread will require you to enforce a proper lifespan for the object, either by thread synchronization or (for heap objects) by handing off the cleanup responsibility to the thread itself...the old hari-kari "delete this;".
|
|
|
|
|
The example you showed would be coded only by a bad programmer in my opinion. And if that's so no amount of safety precautions will save his code
But I agree with you that passing this does not ensure safety by any means!
Nish
Author of the romantic comedy
Summer Love and Some more Cricket [New Win]
|
|
|
|
|
You can't guarantee that the Dlg class or its members will be threadsafe. In other words, you should probably use a mutex or something to protect two threads from accessing/changing the class or its members at the same time. Just FYI.
|
|
|
|
|
Hi, excuse my english...
Why don't you use:
UINT CThreadDemoDlg::StartThread (LPVOID param)
{
CThreadDemoDlg* pD = (CThreadDemoDlg*)param;
...
void CThreadDemoDlg::OnStart()
{
//call the thread on a button action or menu
AfxBeginThread (StartThread, this);
}
|
|
|
|
|
Ditto. The threadstruct is worthless.. in fact, it makes the code less reliable, since the temporary threadstuct has to be deleted after the thread completes... which is not shown in this code.
|
|
|
|
|
While your method is better than the author's threadstruct, both ways require the member function to be a callback. Standard member funcs have a different calling convention, which means you can corrupt the stack if you don't declare the member as static stdcall.
The method I usually use is to pass a stub function a ptr to the member func and the this ptr. This is a hair slower, as the stub has to call the member. But it allows the member to be declared normally, and allows natural access to the 'this' ptr in the member. If you're starting threads into objects written by other team members, for instance, this will let you do so without modifying their code.
|
|
|
|
|
The
CThreadDemoDlg* pD = (CThreadDemoDlg*)param;
is indeed a good idea. While I was trying to get this problem solved, I tried several methods including this. But I prefered the struct because I can pass other params too.
Mike, I also tried your method because I found it on several web sites but I always got a compiler error. Maybe you can give me a code example that works for you because I really liked this method.
Enis Arif
|
|
|
|