创建一个C++线程类:C++中独立于平台的线程

创建一个C++线程类:C++中独立于平台的线程


    简介
    最近,有人问到,是否有一种简单的方法创建一个C++类,以便有助于编写面向对象的线程,而且这个线程类还要有以下属性:


支持事件驱动及基于间隔的异步线程。
支持生成同种及特定的线程。
提供一个FCFS(先来先服务)的堆栈队列用于发送及处理多任务。
可移植。
易于实现。


    为支持新类CThread,还需要编写出其他相关的类,这些类包括:CMutexClass、CEventClass、CTask,CMutexClass及CEventClass提供资源管理,而CTask是一个用于派生的基类,其支持同种异步线程。


    什么是线程?
    每个进程至少有一个控制线程,且每个进程在同一时间至少能执行一项任务,有多种控制线程的进程称为多线程进程,一个多线程进程可在自身环境内异步运行多个任务。


    资源管理——线程同步
    因为进程内的线程分享相同的资源,所以需要在系统级别上设置控制机制来保证数据的完整性。当一个线程修改了某个变量,而另一个线程试图读取它时,或者两个线程同时修改同一变量,就会影响到数据的完整性,为防止这个问题,操作系统提供了一种相互排斥对象,简写为mutex。在多线程程序中,mutex是通过编程来实现的,可防止多个线程在同一时间访问同一资源。当一个线程需要访问某一资源时,它必须先请求一个mutex,一旦线程得到一个mutex,其他想获取同一mutex的线程被阻塞,并处于低CPU占用的等待状态;一旦这个线程完成了数据访问,它会释放对应的mutex,这就允许其他线程获取以访问相关的数据。
    如果mutex实现得不好,将会导致资源饥饿,也就是常说的死锁。资源饥饿发生在当一个或多个线程竞争同一资源时,如果一个线程请求一个mutex两次,也可能会发生死锁。


<!-- @page { margin: 2cm } TD P { margin-bottom: 0cm } P { margin-bottom: 0.21cm } -->

线程A

线程B

请求mutex1修改数据项1

请求mutex2修改数据项2

想要mutex2查看数据项2

想要mutex1查看数据项1




    在上表中,就会发生死锁,因为线程A想要获取mutex2时被阻塞,而它正被线程B所持有;线程B想要获取mutex1时被阻塞,而它正被线程A所持有。
    与mutex类似,Unix中的条件变量,也是一种同步机制。条件变量允许线程会合,可让一个线程在有变化时通知另一个线程,这在Windows中,被称为events。


    操作系统的调用
    下表列出的函数均可用于在CMutexClass、CEventClass、CTask及CThread类中实现线程。

<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->

函数

操作系统

描述

使用的类

CreateThread

Windows

创建一个Windows线程

CThread

pthread_create

UNIX - POSIX THREADS

创建一个UNIX线程

CThread

pthread_join

UNIX - POSIX THREADS

等待一个UNIX线程结束

CThread

pthread_attr_init

UNIX - POSIX THREADS

将某一线程属性结构设为默认

CThread

pthread_attr_setstacksize

UNIX - POSIX THREADS

设置线程属性结构的堆栈大小

CThread

WaitForSingleObject

Windows

等待一个对象有信号

CThread, CMutexClass, CEventClass

CreateMutex

Windows

创建一个命名或未命名的mutex

CMutexClass

CloseHandle

Windows

关闭某一Windows句柄以释放资源

CMutexClass, CEventClass, CThread

ReleaseMutex

Windows

释放某一之前获取,并由WaitForSingleObject锁定的 mutex

CMutexClass, CEventClass

pthread_mutexattr_init

UNIX - POSIX THREADS

初始化某一mutex属性结构

CMutexClass, CEventClass

pthread_mutex_init

UNIX - POSIX THREADS

用给定的属性结构初始化某一mutex

CMutexClass, CEventClass

pthread_mutex_lock

UNIX - POSIX THREADS

锁定某一mutex

CMutexClass, CEventClass

pthread_mutex_unlock

UNIX - POSIX THREADS

解锁某一之前由pthread_mutex_lock锁定的mutex

CMutexClass, CEventClass

pthread_mutex_destroy

UNIX - POSIX THREADS

释放分配给mutex的资源

CMutexClass, CEventClass

CreateEvent

Windows

创建一个Windows事件对象

CEventClass

SetEvent

Windows

设置某一Windows事件对象为有信号状态

CEventClass

pthread_cond_signal

UNIX - POSIX THREADS

解除由pthread_cond_wait阻塞的某一线程

CEventClass

pthread_cond_wait

UNIX - POSIX THREADS

基于条件变量进行阻塞

CEventClass

pthread_cond_init

UNIX - POSIX THREADS

初始化一个条件变量

CEventClass





    类CMutexClass
    类CMutexClass封装了系统级的mutex函数及一个mutex同步对象,且提供了两个成员函数:Lock及Unlock。Lock成员函数锁定一个mutex并把它赋给一个调用线程,mutex一直保持锁定状态直到调用线程使用Unlock成员函数释放它。任何调用Lock成员函数想获取锁定mutex的线程将被阻塞,并置于低CPU占用的等待状态,直到有线程释放了mutex。


CMutexClass::CMutexClass(void):m_bCreated(TRUE)
{
#ifdef WINDOWS
   m_mutex = CreateMutex(NULL,FALSE,NULL);
   if( !m_mutex ) m_bCreated = FALSE;
#else
   pthread_mutexattr_t mattr;

   pthread_mutexattr_init( &mattr );
   pthread_mutex_init(&m_mutex,&mattr);

#endif
   memset(&m_owner,0,sizeof(ThreadId_t));

}

CMutexClass::~CMutexClass(void)
{
#ifdef WINDOWS
   WaitForSingleObject(m_mutex,INFINITE);
   CloseHandle(m_mutex);
#else
   pthread_mutex_lock(&m_mutex);
   pthread_mutex_unlock(&m_mutex);
   pthread_mutex_destroy(&m_mutex);
#endif
}

/**
 *
 * Lock
 * 同一线程只能锁定同一mutex一次
 *
 **/
void
CMutexClass::Lock()
{
   ThreadId_t id = CThread::ThreadId();
   try {
      if(CThread::ThreadIdsEqual(&m_owner,&id) )
         // mutex已被这个线程锁定
         throw "/n/t the same thread can not acquire a mutex twice!/n";
#ifdef WINDOWS
      WaitForSingleObject(m_mutex,INFINITE);
#else
      pthread_mutex_lock(&m_mutex);
#endif
      m_owner = CThread::ThreadId();
   }
   catch( char *psz )
   {
#ifdef WINDOWS
      MessageBoxA(NULL,&psz[2],"Fatal exception CMutexClass::Lock",
                  MB_ICONHAND);
      exit(-1);
#else
      cerr << "Fatal exception CMutexClass::Lock : " << psz;
#endif


   }

}

/**
 *
 * Unlock
 * 只有获取该mutex的线程能释放它
 *
 **/
void
CMutexClass::Unlock()
{
   ThreadId_t id = CThread::ThreadId();
   try
   {
      if( ! CThread::ThreadIdsEqual(&id,&m_owner) )
      throw "/n/t only the thread that acquires a mutex can
             release it!";

      memset(&m_owner,0,sizeof(ThreadId_t));
#ifdef WINDOWS
      ReleaseMutex(m_mutex);
#else
      pthread_mutex_unlock(&m_mutex);
#endif
   }
   catch ( char *psz)
   {
#ifdef WINDOWS
      MessageBoxA(NULL,&psz[2],"Fatal exception CMutexClass::Unlock",
                  MB_ICONHAND);
      exit(-1);
#else
      cerr << "Fatal exception CMutexClass::Unlock : " << psz;
#endif

   }
}


<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->

函数

描述

void CMutexClass()

构造函数

void Lock()

锁定mutex对象或当它阻塞时等待

void Unlock()

解锁之前阻塞的mutex




int g_iStorage = 0;
CMutexClass MyMutex;

void StoreValue( int *pInt )
{
   MyMutex.Lock();           //它可是“掌门人”喔,一个只允许一个线程。

      g_iStorage = *pInt;    //受保护的数据,关键代码段。

   MyMutex.Unlock();         //解锁,允许其他线程访问g_iStorage
}


    类CEventClass
    类CEventClass封装了Windows事件函数、Windows事件对象、Unix条件变量函数、Unix条件变量。集成到CEventClass类中的函数分别为Windows下的SetEvent及CreateEvent,Unix下的htread_cond_init、pthread_cond_destroy、pthread_cond_signal、pthread_cond_wait。在Unix下,为了简单起见,事件同步对象被称为条件变量,在此我们把条件变量及事件对象均称为事件对象。


#include "Thread.h"


#include <iostream>
using namespace std;

CEventClass::CEventClass(void):m_bCreated(TRUE)
{
   memset(&m_owner,0,sizeof(ThreadId_t));
#ifdef WINDOWS
   m_event = CreateEvent(NULL,FALSE,FALSE,NULL);
   if( !m_event )
   {
      m_bCreated = FALSE;
   }
#else
   pthread_mutexattr_t mattr;

   pthread_mutexattr_init(&mattr);
   pthread_mutex_init(&m_lock,&mattr);
   pthread_cond_init(&m_ready,NULL);

#endif
}

CEventClass::~CEventClass(void)
{
#ifdef WINDOWS
   CloseHandle(m_event);
#else
   pthread_cond_destroy(&m_ready);
   pthread_mutex_destroy(&m_lock);
#endif
}


/**
 *
 * Set
 * 设置某一事件为有信号状态
 *
 **/
void
CEventClass::Set()
{
#ifdef WINDOWS
   SetEvent(m_event);
#else
   pthread_cond_signal(&m_ready);
#endif
}

/**
 *
 * Wait
 * 等待一个事件对象为有信号状态,必须在同一线程中与Reset调用成对使用。
 *
 **/
BOOL
CEventClass::Wait()
{

   try
   {
      ThreadId_t id = CThread::ThreadId();
      if( CThread::ThreadIdsEqual(&id,&m_owner) )
      {
         throw "/n/t invalid Wait call, Wait can not be called more
                than once"
            "/n/t without a corresponding call to Reset!/n";
      }
      ThreadId_t zero;
      memset(&zero,0,sizeof(ThreadId_t));

      if( memcmp(&zero,&m_owner,sizeof(ThreadId_t)) != 0 )
      {
         throw "/n/t another thread is already waiting on this event!/n";
      }

      m_owner = CThread::ThreadId();
#ifdef WINDOWS
      if( WaitForSingleObject(m_event,INFINITE) != WAIT_OBJECT_0 )
      {
         return FALSE;
      }
#else
      pthread_mutex_lock(&m_lock);
      pthread_cond_wait(&m_ready,&m_lock);
      return TRUE;
#endif
   }
   catch( char *psz )
   {
#ifdef WINDOWS
      MessageBoxA(NULL,&psz[2],"Fatal exception CEventClass::Wait",
                  MB_ICONHAND);
      exit(-1);
#else
      cerr << "Fatal exception CEventClass::Wait: " << psz;
#endif

   }
   return TRUE;
}

/**
 *
 * Reset
 * 复位一个事件标志为无信号状态,必须在同一线程中与前面的Wait成对使用。
 *
 **/
void
CEventClass::Reset()
{
   try
   {
      ThreadId_t id = CThread::ThreadId();
      if( !CThread::ThreadIdsEqual(&id,&m_owner) )
      {
         throw "/n/t unbalanced call to Reset, Reset must be called
                from/n"
            "/n/t the same Wait-Reset pair!/n";
      }

      memset(&m_owner,0,sizeof(ThreadId_t));

#ifndef WINDOWS
      pthread_mutex_unlock(&m_lock);
#endif
   }
   catch( char *psz )
   {
#ifdef WINDOWS
      MessageBoxA(NULL,&psz[2],"Fatal exception CEventClass::Reset",
                  MB_ICONHAND);
      exit(-1);
#else
      cerr << "Fatal exception CEventClass::Reset: " << psz;
#endif

   }
}


<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->

函数

描述

void Set()

设置一个事件状态为有信号,通知阻塞的线程。

BOOL Wait()

把调用线程置于阻塞状态,直到事件状态为有信号。成功返回TRUE,否则返回FALSE

void Reset()

把一个有信号事件重置为无信号状态。




    接收信号的线程使用事件对象的例子:


CEventClass event;
   .
   .
//线程代码
   .
   .
   while(bContinueRunning)
   {

      event.Wait();     // 等待事件发生

      // 执行某些任务
       .
       .
      event.Reset();    // 重置事件为无信号状态
   }
   .
   .


    向另一线程发信号时使用事件对象的例子:


CEventClass event;
   .
   .
// 改动了一些数据
   .
   .
   event.Set();    // 通知线程一个事件已发生,设置事件为有信号状态。
   .
   .


    CTask类及非特定的线程
    在许多线程编程的示例中,线程处理的数据一般都放在由mutex保护的全局变量中,操纵数据的指令也在集成进线程函数中,我们把这种形式的的线程称为特定异步线程(SAT);理想上来说,数据及对应的处理数据的功能,都应封装进同一对象,我们把这种形式的线程称为同种异步线程(HAT)。在HAT模式下,线程不是特定的,举例来说,HAT中就没有打印线程及I/O线程,取而代之的是,单一线程能执行这两种任务,因为任务是彻底作为对象来实现的,那就是说,它们包含了数据及必要的功能。CTask类就是一个采用HAT线程模式的基类。


typedef enum {
   TaskStatusNotSubmitted,
   TaskStatusWaitingOnQueue,
   TaskStatusBeingProcessed,
   TaskStatusCompleted } TaskStatus_t;

class CTask
{
private:
   CMutexClass m_mutex;
   TaskStatus_t m_state;
   ThreadId_t m_dwThread;
public:
   void SetTaskStatus(TaskStatus_t state)
   {
      m_mutex.Lock();
         m_state=state;
      m_mutex.Unlock();
   }

   void SetId(ThreadId_t *pid)
   {
      memcpy(&m_dwThread,pid,sizeof(ThreadId_t));
   }

   /**
    *
    * Wait
    * 等待任务完成,或timeoutSeconds秒
    *
   **/
   BOOL Wait(int timeoutSeconds)
   {
      timeoutSeconds = timeoutSeconds * 1000;
      if( Status() != TaskStatusCompleted &&
          timeoutSeconds > 0 )
      {
         Sleep(100);
         timeoutSeconds = timeoutSeconds - 100;
      }
      if( Status() == TaskStatusCompleted ) return TRUE;
      return FALSE;
   }

   /**
    *
    * 在此返回任务的当前状态
    *
    **/
   TaskStatus_t Status()
   {
      TaskStatus_t state ;

      m_mutex.Lock();
        state = m_state;
      m_mutex.Unlock();
      return state;
   }

   void Thread(ThreadId_t *pId)
   {
      memcpy(pId,&m_dwThread,sizeof(ThreadId_t));
   }

   CTask(){m_state=TaskStatusNotSubmitted;
           memset(&m_dwThread,sizeof(ThreadId_t),0); }
   ~CTask(){}
   virtual BOOL Task()=0;
};


<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->

函数

描述

m_mutex

Mutex类型的同步对象

virtual BOOL Task()

由一CThread对象调用执行相应任务

TaskStatus_t Status()

确定任务状态:TaskStatusNotSubmittedTaskStatusWaitingOnQueueTaskStatusBeingProcessedTaskStatusCompleted

void Thread(ThreadId_t *pid)

返回处理线程的线程ID

BOOL Wait(int iTimeInSeconds)

将一个调用线程置于等待状态,直到任务结束或iTimeInSeconds时间到。如果一个任务在iTimeInSeconds时间内未完成,将返回FALSE,否则返回TRUE




    在此没有定义CThread类,定不定义它与理解其与CTask对象怎样交互并无关系,下表列出了两者是如何交互的:


处理CTask对象的步骤:

一个CTask对象被传递给CThread对象以便进行处理。
CThread对象将CTask对象放置于一个先来先服务队列中。
CThread对象将CTask对象状态设为 TaskStatusWaitingOnQueue。
CThread对象将CTask对象弹出等待队列。
CThread对象将CTask对象状态修改为 TaskStatusBeingProcessed。
CThread对象调用CTask对象的成员函数task执行相应任务。
CThread对象将CTask对象状态修改为 TaskStateCompleted。


    下面是CThread类的函数列表:


<!-- @page { margin: 2cm } P { margin-bottom: 0.21cm } -->

函数

描述

void CThread()

构造函数,初始化对象数据并启动线程。

void ~CThread()

析构函数,结束线程并释放资源。

BOOL Event(LPVOID lpvData)

放置一数据块在事件堆栈或队列中,并通知对象线程要处理的数据已经准备好了。

BOOL Event(CTask *pTask)

放置一CTask对象在事件堆栈或队列中,并通知对象线程任务正待被执行。

int GetEventsPending()

返回事件堆栈中等待的事件数。

ThreadId_t GetId()

返回对象线程ID

DWORD GetErrorFlags()

返回对象的错误标志。如果没有错误,会返回一个零值(NO_ERRORS);如果有错误,就会设置以下的标志位:MUTEX_CREATION(未创建mutex对象)、EVENT_CREATION(未创建事件对象)、THREAD_CREATION(未创建对象的线程)、ILLEGAL_USE_OF_EVENT(非法使用Event成员函数)。

BOOL PingThread(DWORD dwTimeoutMilli)

判断对象的线程是否在运行。如果在运行返回TRUE,否则返回FALSE,超时以秒为单位。

SetPriority(DWORD dwPriority)

设置线程的优先级,只在Windows平台上有效。

BOOL Start()

开始对象线程。

BOOL Stop()

停止对象线程。

void SetIdle(DWORD dwIdle)

以毫秒为单位修改线程的空闲时间。

SetThreadType(ThreadType_t typ,DWORD dwIdle)

修改线程类型为ThreadTypeEventDrivenThreadTypeIntervalDriven

m_mutex

用于同步的mutex对象,参见CMutexClass

ThreadState_t ThreadState()

返回线程状态:ThreadStateBusy(线程正在处理一个事件)、ThreadStateWaiting(线程正在等等一个新事件)、ThreadStateDown(线程未运行)、ThreadStateShutingDown(线程正在关闭)。





    了解了支撑类之后,下面来看一下主类CThread,即最主要的工作线程类。CThread支持两种类型的线程:事件驱动的及间隔驱动的。事件驱动的线程是一种阻塞于事件对象、处于等待状态的线程,直至事件对象的状态由无信号转为有信号,当另一个线程把某项任务放入CThread对象的队列中时,一个新的事件就发生了,且会通过设置事件对象为有信号来通知对象线程。一旦有信号之后,线程被唤醒并从事件队列中弹出相应的任务,直至队列为空。
    CThread对象为每个任务都调用OnTask成员函数,任务以先来先服务(FDFS)的顺序被执行,因此,第一个放入CThread对象队列中的任务会首先被执行,接着是第二个,如此下去。此时,mutex对象起着队列访问的同步作用,可在线程正在执行某项任务时,把另一个事件放入到队列中来。一旦队列为空,线程则重置事件对象为无信号状态并返回等待下一个事件对象。CThread类支持两种类型的事件驱动线程:特定的及非特定的,参见CTask。
为实现一个特定的线程,必须从CThread类派生出一个新类,这个派生的类应包括OnTask的一个重定义的实现,以处理对象数据类型。


#include "Thread.h"
class CIncrementThread : public CThread
{
public:
   int counter;

   virtual BOOL OnTask( LPVOID lpv )
   {
      ThreadId_t id;

      GetId(&id);
      if( lpv )
      {
         int *pInt = (int *)lpv;

         //在此不能使用cout,输出会因为线程的原因而有所间断
         printf("/tthread(%ld, counter+%d=%d, counter incremented/n",
                id,*pInt,(counter+=*pInt));
      }
      return TRUE;
   }

   virtual BOOL OnTask()
   {
      ThreadId_t id;

      GetId(&id);
      //在此不能使用cout,输出会因为线程的原因而有所间断
      m_mutex.Lock();    //保护计数变量
         printf("/tthread(%ld, counter++= %d, counter incremented)/n",
                id,(++counter));
      m_mutex.Unlock();


      return TRUE;
   }

      int GetValue()
      {
         int counterValue = 0;
         m_mutex.Lock();    //保护计数变量
            counterValue = counter;
         m_mutex.Unlock();
         return counter;
      }

      void Reset()
      {
         m_mutex.Lock();
             counter = 0;
          m_mutex.Unlock();
      }

   CIncrementThread(){counter=0;}
   ~CIncrementThread(){}
};

int main( int argc,
          char *argv[])
{
   //分配对象并开始线程
   CIncrementThread MyThread;
   int two=2;

   while( MyThread.GetValue() < 20 )
   {
      MyThread.Event();    //增量加1
      Sleep(100);          //暂停根线程100毫秒
   }

   MyThread.Reset();
   while( MyThread.GetValue() < 40 )
   {
      MyThread.Event(&two);
      Sleep(100);
   }
}


OUTPUT:
        thread(5220, counter++= 1, counter incremented)
        thread(5220, counter++= 2, counter incremented)
        thread(5220, counter++= 3, counter incremented)
        thread(5220, counter++= 4, counter incremented)
        thread(5220, counter++= 5, counter incremented)
        thread(5220, counter++= 6, counter incremented)
        thread(5220, counter++= 7, counter incremented)
        thread(5220, counter++= 8, counter incremented)
        thread(5220, counter++= 9, counter incremented)
        thread(5220, counter++= 10, counter incremented)
        thread(5220, counter++= 11, counter incremented)
        thread(5220, counter++= 12, counter incremented)
        thread(5220, counter++= 13, counter incremented)
        thread(5220, counter++= 14, counter incremented)
        thread(5220, counter++= 15, counter incremented)
        thread(5220, counter++= 16, counter incremented)
        thread(5220, counter++= 17, counter incremented)
        thread(5220, counter++= 18, counter incremented)
        thread(5220, counter++= 19, counter incremented)
        thread(5220, counter++= 20, counter incremented)
        thread(5220, counter+2=2, counter incremented
        thread(5220, counter+2=4, counter incremented
        thread(5220, counter+2=6, counter incremented
        thread(5220, counter+2=8, counter incremented
        thread(5220, counter+2=10, counter incremented
        thread(5220, counter+2=12, counter incremented
        thread(5220, counter+2=14, counter incremented
        thread(5220, counter+2=16, counter incremented
        thread(5220, counter+2=18, counter incremented
        thread(5220, counter+2=20, counter incremented
        thread(5220, counter+2=22, counter incremented
        thread(5220, counter+2=24, counter incremented
        thread(5220, counter+2=26, counter incremented
        thread(5220, counter+2=28, counter incremented
        thread(5220, counter+2=30, counter incremented
        thread(5220, counter+2=32, counter incremented
        thread(5220, counter+2=34, counter incremented
        thread(5220, counter+2=36, counter incremented
        thread(5220, counter+2=38, counter incremented
        thread(5220, counter+2=40, counter incremented


在上面的例子中,从CThread类中派生了一个CIncrementThread类,且重定义了OnTask()及OnTask(LPVOID)虚成员函数。在OnTask()的实现中,添加了一个对象计数变量,而OnTask的另一个成员函数接受一个指向整型值的指针,并将指针值添加到计数成员变量中。这个例子演示了一个线程可处理的两类事件,又因为计数变量可能会被不止一个线程访问,所以用了CThread::m_mutex对象来保证访问的独占性。
另外,在此的HAT是由CThread及CTask两者来实现的。


#include "Thread.h"
class CTaskIncrementer: public CTask
{
private:
   int counter;
   int incr;
public:
   void SetIncr(int iValue)
   {
      m_mutex.Lock();
         incr = iValue;
      m_mutex.Unlock();
   }

   int GetIncrementValue()
   {
      int incrValue;
      m_mutex.Lock();
         incrValue=incr;
      m_mutex.Unlock();
         return incrValue;
   }

   int GetValue()
   {
      int counterValue = 0;
      m_mutex.Lock();    //保护计数变量
         counterValue = counter;
      m_mutex.Unlock();
         return counter;
   }

   BOOL Task()
   {
      ThreadId_t id;

      Thread(&id);

      m_mutex.Lock();
         printf("/tthread(%ld, counter+%d=%d, counter incremented/n",
                id,incr,(counter+=incr));
      m_mutex.Unlock();
         return TRUE;
   }
   CTaskIncrementer(){counter=0;}
   ~CTaskIncrementer(){}
};

int
main(int argc,
   char *argv[])
{
   CTaskIncrementer incr;
   CThread thr;

   incr.SetIncr(2);
   while( incr.GetValue() < 40 ) thr.Event(&incr);
}

OUTPUT:
       thread(5700, counter+2=2, counter incremented
       thread(5700, counter+2=4, counter incremented
       thread(5700, counter+2=6, counter incremented
       thread(5700, counter+2=8, counter incremented
       thread(5700, counter+2=10, counter incremented
       thread(5700, counter+2=12, counter incremented
       thread(5700, counter+2=14, counter incremented
       thread(5700, counter+2=16, counter incremented
       thread(5700, counter+2=18, counter incremented
       thread(5700, counter+2=20, counter incremented
       thread(5700, counter+2=22, counter incremented
       thread(5700, counter+2=24, counter incremented
       thread(5700, counter+2=26, counter incremented
       thread(5700, counter+2=28, counter incremented
       thread(5700, counter+2=30, counter incremented
       thread(5700, counter+2=32, counter incremented
       thread(5700, counter+2=34, counter incremented
       thread(5700, counter+2=36, counter incremented
       thread(5700, counter+2=38, counter incremented
       thread(5700, counter+2=40, counter incremented


间隔驱动的线程是一种在预先定义的间隔时间上被唤醒的线程,它会检查所在环境是否发生了变化,并处理这些变化,之后休眠等待下一个间隔时间,接着再唤醒做同样的事。要实现一个间隔驱动的线程,需派生一个CThread类并重定义OnTask(LPVOID)。一旦线程实例化后,就可以用ThreadTypeIntervalDriven参数来调用SetThreadType成员函数,设置好以毫秒计的间隔。


#include "Thread.h"

class CIncrementThread : public CThread
{
public:
   int counter;

   virtual BOOL OnTask()
   {
      ThreadId_t id;

      GetId(&id);
      //在此不能使用cout,输出会因为线程的原因而有所间断
      m_mutex.Lock();    //保护计数变量
         printf("/tthread(%ld, counter++= %d, counter incremented)/n",
                id,(++counter));
      m_mutex.Unlock();


      return TRUE;
   }

   int GetValue()
   {
      int counterValue = 0;
      m_mutex.Lock();    //保护计数变量
         counterValue = counter;
      m_mutex.Unlock();
      return counter;
   }

   void Reset()
        {
            m_mutex.Lock();
               counter = 0;
            m_mutex.Unlock();
        }

   CIncrementThread(){counter=0;}
   ~CIncrementThread(){}
};

int
main( int argc,
    char *argv[] )
{
   CIncrementThread thr;

   thr->SetThreadType(ThreadTypeIntervalDriven,100);
   Sleep(500);

}

OUTPUT:
        thread(6104, counter++= 12, counter incremented)
        thread(6104, counter++= 13, counter incremented)
        thread(6104, counter++= 14, counter incremented)
        thread(6104, counter++= 15, counter incremented)
        thread(6104, counter++= 16, counter incremented)


    结论
以上就是一个全功能的线程对象,且在Linux上做过测试,但没有在SunOS及其他Unix平台上测试过,不过应该也没问题。当在Windows上编译时,一定要代码生成选项上指定/Mt或/Mtd,表明你的程序是一个多线程程序,而下面则是Linux上的make文件。


CC=g++
LIBS=-lpthread -lrt
CFLAGS=-DSUNOS -DNANO_SECOND_SLEEP

OBJS=Thread.cpp EventClass.cpp MutexClass.cpp main.cpp


EXECS = thread

all: $(EXECS)

thread: $(OBJS)
    $(CC) $(CFLAGS) -o thread $(OBJS) $(LIBS)


clean:; rm -f *.o $(EXECS)

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值