C++ 线程类






2.95/5 (34投票s)
一篇关于包装Win32线程API的文章。
引言
C++语言鼓励面向对象编程。Win32 API完全基于C语言。编写Windows平台的软件总是需要使用Win32 API。许多偏好C++和面向对象编程的开发者都希望有合适的C++类库可用,以使他们的软件具有始终如一的面向对象外观和感觉。
Java以及现在的.NET的巨大流行,主要基于构成编程平台的大量类。Java和.NET应用程序程序员只需利用这些类编写他们的应用程序,而相比之下,C++程序员首先编写基础架构,然后用它来编写应用程序。在本文中,我将向您展示如何编写一个简单的C++类来包装Win32线程相关的API。
Thread类
Java和.NET平台已经提出了一些非常好的模型,所以我们不妨使我们的模型看起来相似。这样做的好处是,任何熟悉Java或.NET的人都可以很容易地理解它。
Java和.NET中的线程模型要求线程对象接受类方法作为其线程过程。这是一个示例
Java中的线程
// define class with a threadable method
public class MyObject implements Runnable {
// the thread procedure
public void run() {
// TODO: put the code here
}
}
MyObject obj = new MyObject();
Thread thread = new Thread(obj);
tread.start();
.NET中的线程
// define class with a threadable method
public class MyObject {
// the thread procedure
public void Run() {
// TODO: put the code here
}
}
MyObject obj = new MyObject();
Thread thread = new Thread(new ThreadStart(obj.Run));
tread.Start();
这些模型非常相似。Java要求可线程化对象实现Runnable
接口,而.NET某种程度上也要求这样做,因为这两个平台上的Thread
类都期望可线程化过程具有以下形式:public void run()
。
Java规范相当简单。只有一个简单的接口公开一个简单的方法。.NET规范更加复杂。“委托”的概念为编写多线程程序提供了更大的灵活性。这是一个示例
// create a threadable object
public class MyObject {
// first thread procedure
public void ThreadProc1() {
// TODO:
}
// second thread procedure
public void ThreadProc2() {
// TODO:
}
}
MyObject obj = new MyObject();
// create first thread
Thread thread1 = new Thread( new ThreadStart(obj.ThreadProc1) );
thread1.Start();
//create second thread
Thread thread2 = new Thread( new ThreadStart(obj.ThreadProc2) );
thread2.Start();
.NET线程模型提供了更多优势。任何与ThreadStart
委托兼容的类方法都可以作为线程过程运行。如上面的代码片段所示,多个线程可以并发访问和操作单个对象实例。这是一个非常强大的功能。
我们自然希望C++线程模型像Java一样简单,像.NET一样灵活。让我们首先关注Java式的简单性。这是一个建议
// define the interface struct IRunnable { virtual void run() = 0; }; // define the thread class class Thread { public: Thread(IRunnable *ptr) { _threadObj = ptr; } void start() { // use the Win32 API here DWORD threadID; ::CreateThread(0, 0, threadProc, _threadObj, 0, &threadID); } protected: // Win32 compatible thread parameter and procedure IRunnable *_threadObj; static unsigned long __stdcall threadProc(void* ptr) { ((IRunnable*)ptr)->run(); return 0; } };
现在我们可以像Java程序员一样优雅地编写多线程程序了。
// define class with a threadable method class MyObject : IRunnable { public: // the thread procedure virtual void run() { // TODO: put the code here } } MyObject *obj = new MyObject(); Thread thread = new Thread(obj); tread->start();
它之所以如此简单,是因为我们将Win32 API调用隐藏到一个包装类中。这里的巧妙之处在于作为我们Thread
类一部分定义的静态方法。因此,我们模拟了更简单的Java Thread
类。
.NET的Thread
和ThreadStart
方法稍微难以模拟。但是我们仍然可以通过使用指向类方法的指针来实现它。这是一个例子
// define class with a threadable method class MyObject : IRunnable { // pointer to a class method typedef void (MyObject::* PROC)(); PROC fp; // first thread procedure void threadProc1() { //TODO: code for this thread procedure } // second thread procedure void threadProc2() { //TODO: code for this thread procedure } public: MyObject() { fp = threadProc1; } void setThreadProc(int n) { if(n == 1) fp = threadProc1; else if(n == 2) fp = threadProc2; } // the thread procedure virtual void run() { (this->*fp)(); } }; MyObject *obj = new MyObject(); obj->setThreadProc(1); Thread *thread1 = new Thread(obj); thread1->start(); obj->setThreadProc(2); Thread *thread2 = new Thread(obj); thread2->start();
实际的可线程化方法run()
现在使用指向类方法的指针来运行相应的线程过程。在启动新线程之前,必须正确初始化该指针。
结论
将Win32 API包装到C++类中是首选方法。Java和.NET平台为我们提供了定义良好的模型。相比之下,这些模型非常相似,因此为线程类、套接字类、流类等定义C++类应该只是遵循提供的文档的问题。
您可以下载Thread
类并试用它。我设计它尽可能简单,但您可以通过包装其他一些与线程相关的API来增强它,例如SetThreadPriority, GetThreadPriority
等。