Better threading






2.68/5 (11 votes)
A more object-oriented way for multi-threading (not bound to MFC).
Introduction
Have you noticed how often we ignore object-oriented concepts when doing even very simple multithreading? There are two typical examples:
- You code a pure Win32 project in C++, and you just don't have time to devise some object-oriented thread API wrapper, so you simply create thread procedures here and there, eventually making the code unreadable.
- You code an MFC application, and you need a worker thread. What do you do? Sure thing, you call
AfxBeginThread
, and (again) pass in a pointer to a thread procedure, ruining the object-oriented nature of the application.
Threads are fairly independent entities in an application, and as such, they must be separated from other implementations. The place where a thread procedure belongs least of all is a CDialog
-derived class.
That's why I decided to make a thin wrapper class for a basic thread API. I use this class regularly, and I think it makes my applications better.
Using the Code
The code is actually a small class, Thread
. Each instance of this class represents a thread, and the class contains some basic functions to control the thread, including gracefully (or ungracefully, if need be) terminating it.
Let's go through the steps necessary to set up a thread with this class:
- Derive a class from
Thread
. It will be the class that you later instantiate and use. - Override the virtual function called
ThreadProc
. The implementation in your derived class is the substitution for a usual thread procedure. This virtual function must follow some basic rules to work properly. These are: - Never call _
endthread
,_endthreadex
,ExitThread
, and such from withinThreadProc
. Instead, simplyreturn
. - From time to time (as often as possible), call
GetStop
to immediately clean up and return ifGetStop
returnstrue
. See the example below. - Instantiate
Thread
. Make sure you don't create it on the stack because the lifetime of theThread
object must be at least as long as that of the thread itself. A good place for aThread
-derived object is among members in someCWnd
-derived class. - Use the
Thread::Start
member function to start the thread. That's it! - Optionally, you can control the thread from anywhere (including the thread procedure and your main thread) by calling its member functions.
Example of a good Thread
-derived class:
class MyThread : public Thread { protected: virtual unsigned int ThreadProc() { void *pData = GetData(); //in case you'll need it for(int i=0; i<1000; ++i) { if(GetStop()) return 0; Sleep(100); //some processing... } return 0; } };
Some window class in your application could then have a member declared as follows:
protected: MyThread thread;
And finally, some member function of this class could call:
thread.Start(this); //we're passing this pointer as additional data. //This data could then be retrieved from within //the thread by calling GetData()
See the demo application for more details.
Points of Interest
Note that the thread function is a non-static member of the Thread
class. This is something you can't normally do because of the this
pointer implicitly passed to non-static member functions. That's why ThreadProc
gets called not directly, but through a mediator, _ThreadProc
, which is a static class member. The rest of the implementation is pretty straightforward, and deserves no special explanation.
Disclaimer
Use this code at your own risk. Although I'm pretty sure it's safe, I still had to say this :D
Happy coding!
Revision History
- Jan 31, 2007 - Modified the demo application to eliminate
SendMessage
calls across threads. - Jan 30, 2007 - Originally posted.