Thread management class

Well, multi thread is a useful mechanism in many scenarios. But the thread is not easy to control. Here I will discuss a thread management class which provides a connection between thread function and class member function.

 

A common question about the thread in a job interview is that can a class member function works as a thread function? The answer is definitely, but few people know why. The reason is: a non-static member function has an implicit parameter: this, so the prototype is not the same as the thread function.

 

The key point of the Thread management class is that the class separate the working function from the thread function, and implement it in a member function of another class.

 

Here is the implement of Thread management class:

 

The class IRunnable does the main work in the thread. You may derive a class from it with any attributes or parameters you want. The function run() will be called in thread.

// The core interface of this threading implementation.

class IRunnable {

    friend class CThread;

    friend class CSimpleThreadPool;

protected:

    virtual void run() = 0;

};

 

Here a TLS structure is used to communicate with classes, because the run() function and the StartThreadST() are in the same thread context, so they can access to the same TLS allocation, though they are from different classes.

// structure holds Thread Local Storage descriptor

struct TLSDescriptor {

    DWORD descriptor;

 

    TLSDescriptor() {

        descriptor = TlsAlloc();

        if (descriptor == -1) throw "TlsAlloc() failed to create descriptor";

    };

 

    ~TLSDescriptor() { TlsFree(descriptor); };

};

 

And the following is the thread management class.

When a variable is used as a controller to stop thread, it’s better to define the variable as a volatile one.

// Very basic thread class, implementing basic threading API

// the key point: separate the working function from thread function, and implement in a member function in other class.

class CThread: public IRunnable {

private:

    static TLSDescriptor m_TLSDesc;

    volatile bool   m_bIsInterrupted;  // control loop in thread

    volatile bool   m_bIsRunning;      // indicate if thread is running

 

    int             m_nThreadPriority;

    IRunnable       *m_RunObj;  // provide run function by other class

    QMutex          m_qMutex;   // notify weather the thread is running

 

    // This is the thread's API function to be executed. Method executes

    // run() method of the CThread instance passed as parameter.

    static DWORD WINAPI StartThreadST(LPVOID PARAM) {

        CThread *_this = (CThread *) PARAM;

 

        if (_this != NULL) {

            _this->m_qMutex.Lock();    // show the thread is running

 

            // Set the pointer to the instance of the passed CThread

            // in the current Thread's Local Storage.

            // Also seecurrentThread() method.

            TlsSetValue(CThread::m_TLSDesc.descriptor, (LPVOID) _this);

            _this->run();

            _this->m_bIsRunning = false;

            _this->m_qMutex.Unlock();  // notify run() returned

        }

        return 0;

    };

 

protected:

    // It is not possible to instantiate CThread objects directly. Possible only by

    // specifying a IRunnable object to execute its run() method.

    CThread(int nPriority = THREAD_PRIORITY_NORMAL): m_qMutex() {

        this->m_bIsInterrupted = false;

        this->m_bIsRunning = false;

        this->m_nThreadPriority = nPriority;

        this->m_RunObj = NULL;

    };

 

    // this implementation of the run() will execute the passed IRunnable

    // object (if not null). Inheriting class is responsible for using this

    // method or overriding it with a different one.

    virtual void run() {

        if (this->m_RunObj != NULL)

            this->m_RunObj->run();

    };

 

public:

    CThread(IRunnable *RunTask, int nPriority = THREAD_PRIORITY_NORMAL): m_qMutex() {

        this->m_bIsInterrupted = false;

        this->m_bIsRunning = false;

        this->m_nThreadPriority = nPriority;

 

        if (this != RunTask) this->m_RunObj = RunTask;

        else throw "Self referencing not allowed.";

    };

 

    virtual ~CThread() {

        this->interrupt();

        // wait until thread ends

        this->join();

    };

 

    // Method returns the instance of a CThread responsible

    // for the context of the current thread.

    // used only in thread function

    static CThread& currentThread() {

        CThread *thr = (CThread *) TlsGetValue(CThread::m_TLSDesc.descriptor);

        if (thr == NULL) throw "Call is not within a CThread context.";

        return *thr;

    };

 

    // Method signals thread to stop execution.

    void interrupt() { this->m_bIsInterrupted = true; };

 

    // Check if thread was signaled to stop.

    bool isInterrupted() { return this->m_bIsInterrupted; };

 

    // Method will wait for thread's termination.

    void join() {

        this->m_qMutex.Lock();

        this->m_qMutex.Unlock();

    };

 

    // Method starts the Thread. If thread is already started/running, method

    // will simply return.

    void start() {

        HANDLE  hThread;

        LPTHREAD_START_ROUTINE pStartRoutine = &CThread::StartThreadST;

 

        if (this->m_qMutex.TryLock()) {  // test if the thread is running

            if (!this->m_bIsRunning) {

                this->m_bIsRunning = true;

                this->m_bIsInterrupted = false;

 

                hThread = ::CreateThread(NULL, 0, pStartRoutine, (PVOID) this, 0, NULL);

                if (hThread == NULL) {

                   this->m_bIsRunning = false;

                    this->m_qMutex.Unlock();

                   throw "Failed to call CreateThread(). Thread not started.";

                }

 

                ::SetThreadPriority(hThread, this->m_nThreadPriority);

                ::CloseHandle(hThread);

            }

 

            this->m_qMutex.Unlock();

        }

    };

};

 

Steps to use the class.

Firstly device a class from IRunnable:

 

class Increase : public IRunnable

{

private:

    CEdit *m_pEdit;

public:

    Increase(CEdit *pEdit, CThread *pCThread = NULL) {

        m_pEdit = pEdit;

    };

    ~Increase() {};

 

    virtual void run()

    {

        HWND hWin = (AfxGetApp()->m_pMainWnd)->m_hWnd;

        int iIndex = 0;

 

        while (!CThread::currentThread().isInterrupted()) {

 

            PostMessage(hWin, MSG_DATA_INS, (WPARAM)m_pEdit, (LPARAM)iIndex);

            iIndex++;

            Sleep(1000);

        }

    };

};

 

Secondly attach the Increase class into CThread.

void CThreadClassTestDlg::OnBnClickedButton()

{

    Increase *pIncrease = NULL;

    pIncrease = new Increase(&this->m_CEdit);

 

    CThread *pCThread = NULL;

    pCThread = new CThread(pIncrease);

    if (NULL != pCThread)

        pCThread->start();

}

 

When want to stop the thread, just call.

pIncrease ->interrupt();

 

 

And the other way to use the thread class is derive from the CThread directly: class Increase : public CThread, and implement the virtual run()function.

The code is as followed:

void CThreadClassTestDlg::OnBnClickedButton()

{

    Increase *pIncrease = NULL;

    pIncrease = new Increase(&this->m_CEdit);

    if (NULL != pIncrease)

        pIncrease->start();                    

}

 

And with the class we can implement a light weighted thread pool, which I discuss later.

 

Thanks to the original author Ruslan Ciurca, from his source code I do learn a lot.

For more information please link to:

http://www.codeproject.com/KB/IP/IOCP_how_to_cook.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值