通用的 C++ 线程类






3.76/5 (13投票s)
2001年11月15日
4分钟阅读

137758

1930
本文介绍了一个类,该类可以立即用作现有工作线程的容器,也可以(通过继承)用于面向对象的编程。
前言
我使用 Code Project 和 CodeGuru 等代码宝库已经有相当长一段时间了。现在是回报大家的时候了。虽然不是拿出可以直接被所有人或许多人使用的东西。但这是我真诚的开始。
在这篇文章中,我试图找出使用工作线程时缺乏面向对象的可编程性,并克服它。这个项目更具演进性而非革命性。它始于 Ryan Teixeira 关于设计线程类的理论,我在此基础上构建了基本框架。然后,为了满足我当前项目的需求,我必须考虑一些轻微/简单的可移植性限制。
言归正传,让我们继续。
我不会在这里讨论的内容
我假设读者熟悉线程的概念 / 至少听说过或使用过工作线程。我的目标是展示这个类可以如何快速地让你更接近面向对象编程。
类
CThread
类在声明形式上如下所示。我为了达到这个目的,违背了我的编码习惯(我相信这和许多程序员的习惯差不多),并在源文件中对代码进行了大量的注释。class CThread { public: /* * Info: Default Constructor */ CThread(); /* * Info: Plug Constructor */ CThread(LPTHREAD_START_ROUTINE lpExternalRoutine); /* * Info: Default Destructor */ ~CThread(); /* * Info: Starts the thread. */ DWORD Start( void* arg = NULL ); /* * Info: Stops the thread. */ DWORD Stop ( bool bForceKill = false ); /* * Info: Starts the thread. */ DWORD GetExitCode() const; /* * Info: Attaches a Thread Function */ void Attach( LPTHREAD_START_ROUTINE lpThreadFunc ); /* * Info: Detaches the Attached Thread Function */ void Detach( void ); protected: /* * Info: DONT override this method. */ static DWORD WINAPI EntryPoint( LPVOID pArg); /* * Info: Override this method. */ virtual DWORD Run( LPVOID arg ); /* * Info: Constructor-like function. */ virtual void ThreadCtor(); /* * Info: Destructor-like function. */ virtual void ThreadDtor(); private: /* * Info: Thread Context Inner Class * * Every thread object needs to be associated with a set of values. * like UserData Pointer, Handle, Thread ID etc. * * NOTE: This class can be enhanced to varying functionalities * eg., * * Members to hold StackSize * * SECURITY_ATTRIBUTES member. */ class CThreadContext { public: CThreadContext(); /* * Attributes Section */ public: HANDLE m_hThread; // The Thread Handle DWORD m_dwTID; // The Thread ID LPVOID m_pUserData; // The user data pointer LPVOID m_pParent; // The this pointer of the parent CThread object DWORD m_dwExitCode; // The Exit Code of the thread }; /* * Attributes Section */ protected: /* * Info: Members of CThread */ CThreadContext m_ThreadCtx; // The Thread Context member LPTHREAD_START_ROUTINE m_pThreadFunc; // The Worker Thread Function // Pointer };
方法
方法名称 | 描述 |
---|---|
CThread() |
使用内部默认的 EntryPoint 静态函数作为 ThreadFunc。 |
CThread( LPTHREAD_START_ROUTINE pThreadFunc) |
插拔构造函数。这用于代替创建对象然后调用 Attach。 |
~CThread() |
关闭线程对象并销毁线程。如果不想在对象销毁时终止线程,可以对此进行修改。 |
DWORD Start( LPVOID pArg = NULL ) |
此方法启动/创建线程。如果成功则返回 ERROR_SUCCESS,否则返回 GetLastError() 返回的错误值。 |
DWORD Stop( bool bForceKill = false ) |
如果线程已终止,此方法返回其退出代码。该参数可以选择性地允许强制终止线程。 |
DWORD GetExitCode() const |
返回上一个线程的退出代码。 |
void Attach( LPTHREAD_START_ROUTINE lpThreadFunc ) |
我承认,我是在了解 COM 后才借鉴了这一特性的。这是一个很棒的概念。在这里,它用于附加任何工作线程函数,这使得通用类本身可以即时使用。怎么做?我们很快就会看到。 |
void Detach( void ) |
此方法将对象从任何挂钩的工作函数移除,回到默认的 EntryPoint 静态方法。 |
static DWORD WINAPI EntryPoint( LPVOID pArg ) |
此方法是让类包含工作线程所需的唯一技巧,其他部分都非常简单。 |
virtual DWORD Run( LPVOID arg ) |
体方法,此方法应包含你的工作线程代码。注意:签名与你的工作线程相似,并且 arg 是用户数据。 |
virtual void ThreadCtor() |
线程创建作用域的构造函数模拟。你可以重写此方法并编写线程级别的构造/初始化。此方法将在线程创建/启动之前调用。 |
virtual void ThreadDtor() |
线程销毁作用域的析构函数模拟。你可以重写此方法并编写线程级别的取消初始化。此方法将在线程结束后调用。 |
用法
理解任何代码的最佳方式是通过示例。所以我们采用这种方式...
场景 1
这更像是常见场景而不是不寻常的。假设你有一个工作线程函数 "Threaded"
DWORD WINAPI Threaded( LPVOID lpData ) { while(1) { printf("worker threaded code"); Sleep(1000); } }
现在你想以面向对象的方式使用和控制它(即它的作用域、生命周期可见性等)。你可以这样做。
CThread t1; t1.Attach(Threaded); // Threaded is the name of the worker function. t1.Start(); // ... Do something useful t1.Stop(); // Alternatively CThread t2(Threaded); t2.Start(); // ... Do something useful t2.Stop(true); // force kill if running.
这里应该有个陷阱。是的,陷阱在于你失去了 CTOR 和 DTOR 模拟函数 ThreadCTOR() 和 ThreadDTOR() 的功能,并且无法访问封装类的任何成员或方法。但尽管如此,这也不是我们的要求。但如果它们是呢...
场景 2
面向对象的方式。即使花费几分钟,这也是更好的处理方式。
你派生一个类,并且只重写 run 方法,这就足够了。当然,你也可以添加任意多的成员/方法。
class CDemoThread : public CThread { virtual DWORD Run( LPVOID arg ) { while(1) { printf("Threaded Object Code \n"); Sleep(1000); } } };
那么,你如何使用它呢?差别不大。
CDemoThread dmt; dmt.Start(NULL); // ... do something useful dmt.Stop(true);
未来计划
我计划用更多功能更新这个类。如果有人感兴趣或有特别的需求,请告诉我。
结论
我们将拥有面向对象的代码。面向对象编程的好处在早期阶段可能很难完全体会到(不引发任何辩论)。
希望大家都能找到它的用处!