创建一个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() |
确定任务状态:TaskStatusNotSubmitted、TaskStatusWaitingOnQueue、TaskStatusBeingProcessed、TaskStatusCompleted。 |
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) |
修改线程类型为ThreadTypeEventDriven或ThreadTypeIntervalDriven。 |
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)