INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Member Login

HANDLE


PASSWORD
Remember Me
Forgot Password?

Come Join Us!

  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • Turn Off Ad Banners
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!

E-mail*
Handle

Password
Verify P'word
*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Partner With Us!

"Best Of Breed" Forums Add Stickiness To Your Site
Partner Button
(Download This Button Today!)

Member Feedback

"...Keep up the good work - excellent site - i'd been looking for something like this for ages !..."

Geography

Where in the world do Tek-Tips members come from?

Microsoft: Visual C++ FAQ

Multithreading in MFC

Threads in a GUI (MFC) app the easy way
Posted: 25 May 04 (Edited 30 Aug 04)

Every once in a while questions about threading in MFC based apps gets posted.
Since thread handling can be kind of complex the answer if often like
"its too complex to teach in forums like these...".

However, given that you can follow a small set of simple rules, threading and thread's interaction with the GUI doesn't have to be all that complicated.

The rules:
Rule #1: Do not access the same data between the threads.
Rule #2: Do not pass references or pointers to variables between threads.
Rule #3: Use windows messages to interact with the GUI.

Follow these rules and you can write multithreaded MFC applications without having to be a monster thread hacker

So, what do they mean really?

VERY BAD CODE:

CODE

struct ThreadParam
{
    CMyDialog* mDlg;
};

UINT MyThreadProc( LPVOID pParam )
{
    ThreadParam* p = static_cast<ThreadParam*> (pParam);
    p->mDlg->callSomeMethod();
}

void CMyDialog::OnSomeButton()
{
    ...
    ThreadParam param;
    param.mDlg = this;
    AfxBeginThread(MyThreadProc, &param);
    ...
}
The code snipped above violates all 3 rules:

#1 Do not access the same data between the threads.
The code passes the dialog's this pointer to the thread, but accessing isn't thread safe.

#2 Do not pass references or pointers to variables.
The code passes a pointer to the local ThreadParam variable. If OnSomeButton goes out of scope before MyThreadProc tries to access the parameter things will of course go bad.

#3 Use windows messages to interact with the GUI.
Its related to rule #1. You can't do a callSomeMethod() like the code above does, simply because the access to the mDlg isn't safe.
------
So, how should it be done then?

Rule #2: Allocate with new and let the thread delete it when finished.

Rule #1 and #3: Here is a hint: The API functions ::PostMessage and ::SendMessage are thread safe!
That means you can use the windows handle (ie the CWnd::m_hWnd) to interact with your MFC classes.

Here's how one could do it 'the right way':

CODE

// Defining a 'user defined' message for the call I wish to make
#define MYMESS_CALL_SOME_METHOD (WM_APP + 1)

struct ThreadParam
{
    HWND mDlg;    // Note: A handle.
};

UINT MyThreadProc( LPVOID pParam )
{
    ThreadParam* p = static_cast<ThreadParam*> (pParam);
    // Using message to call the method. Note: I could of course use the
    // WPARAM, LPARAM and returned value for something meaningful if I wished.
    ::SendMessage(p->mDlg, MYMESS_CALL_SOME_METHOD, 0, 0);
    delete p;
}

void CMyDialog::OnSomeButton()
{
    ...
    ThreadParam* param = new ThreadParam;
    param->mDlg = m_hWnd;  // A handle, not a dangerous 'this'
    AfxBeginThread(MyThreadProc, param);
    param = 0; // The other thread shall delete it
    ...
}

Handling the user defined message the standard way:

Declare a method in the dialog message map (in the .h file)

CODE

    //{{AFX_MSG(CMyDialog)
    ...
    afx_msg LRESULT OnCallSomeMethod(WPARAM, LPARAM);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()

Map the message to the method and implement the method (in the .cpp file):

CODE

BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
    //{{AFX_MSG_MAP(CMyDialog)
    ...
    ON_MESSAGE(MYMESS_CALL_SOME_METHOD, OnCallSomeMethod)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

LRESULT CMyDialog::OnCallSomeMethod(WPARAM wp, LPARAM lp)
{
    callSomeMethod();
    return 0;
}

With that said there's one tiny little more thing to watch out for: The CString.

CString is clever enough to use the same instance of data:

CODE

CString a ="Foo";
CString b = a;
CString c(a);
All 3 CStrings above will internally point to the same "Foo". That is normally all good and well, but if you remember:
Rule #2: Do not pass references or pointers to variables between threads
you probably realize that passing a CString across threads can violate this because of how stuff is handeled internally.

BAD CODE:

CODE

struct ThreadParam
{
    CString mSomeString;
};
...
CString foo="Foo";
ThreadParam* param = new ThreadParam;
// Looks like a copy, but internally
// param->mSomeString points to foo's internals:
param->mSomeString = foo;  
AfxBeginThread(MyThreadProc, param);

You can simply make sure no such internal pointing occurs by hiding the fact that it's a CString you're copying from:

CODE

// Making sure mSomeString doesn't
// 'see' the CString it's copied from
param->mSomeString = static_cast<LPCTSTR>(foo);

That's about it.

Good Luck!

Back to Microsoft: Visual C++ FAQ Index
Back to Microsoft: Visual C++ Forum
My FAQ Archive
Email This FAQ To A Friend

My Archive