65.9K
CodeProject 正在变化。 阅读更多。
Home

通用的 C++ 线程类

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.76/5 (13投票s)

2001年11月15日

4分钟阅读

viewsIcon

137758

downloadIcon

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);

未来计划

我计划用更多功能更新这个类。如果有人感兴趣或有特别的需求,请告诉我。

结论

我们将拥有面向对象的代码。面向对象编程的好处在早期阶段可能很难完全体会到(不引发任何辩论)。

希望大家都能找到它的用处!

© . All rights reserved.