废话不多说,详细介绍使用线程的优点好处请参考baidu、google。
一、线程使用场景。使用线程的方式大致有两种:
(1)流水线方式。根据业务特点,将一个流程的处理分割成多个线程,形成流水线的处理方式。产生的结果:延长单一流程的处理时间,提高系统整体的吞吐能力。
(2)线程池方式。针对处理时间比较长且没有内蕴状态的线程,使用线程池方式分流消息,加快对线程消息的处理,避免其成为系统瓶颈。
线程使用的关键是 线程消息队列、线程锁、智能指针 的 使用。其中以线程消息队列最为重要。
二、线程消息队列描述。所谓线程消息队列,就是一个普通的循环队列(其它数据结构也未尝不可,具体内容请参考数据结构课本)加上“多生产者-单(多)消费者的PV操作”(详细内容请参考操作系统课本)。流水线方式中的线程是单消费者,线程池方式中的线程是多消费者。
为了后文更好的描述问题,作如下说明:
(1)假定循环队列CircleQueue中,存放的消息指针类型是MyMSG *,入队操作EnQueue,出队操作DeQueue,判断队满IsQueueFull,判断队空IsQueueEmpty。
(2)生产者消费者:生产者线程生产消息(MyMSG *),放在一个空缓冲区(CircleQueue)中,供消费者线程消费,生产者生产消息(EnQueue),如果缓冲区满(IsQueueFull),则被阻塞,消费者消费消息(DeQueue),如果缓冲区空(IsQueueEmpty),则被阻塞。线程消息队列就是生产者消费者问题中的缓冲区,而它的生产者是不限定的,任何线程都可以作为生产者向其中进行EnQueue操作,消费线程则可能是一个,也可能是多个。因此对循环队列的任何操作都要加锁,以保证线程安全。
PV操作和锁机制的基础都是信号量。下面列出posix标准中给出的有关信号量的操作:
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t * sem);
int sem_trywait(sem_t * sem);
int sem_post(sem_t * sem);
int sem_getvalue(sem_t * sem, int * sval);
int sem_destroy(sem_t * sem);
各个函数的详细用法,请在linux/unix上查看man。
三、线程消息队列实现。基于以上讨论,下面给出线程消息队列的实现(仅为了说明问题,非标准可运行代码)。
class ThreadQueue
{
CircleQueue * queue;
int nFull;
int nEmpty;
sem_t lock;
sem_t fullCond;
sem_t emptyCond
};
void createThreadQueue()
{
createCircleQueue(queue);
nFull=0;
nEmpty=0;
sem_ini(&lock,0,1);
sem_init(&fullCond,0,0);
sem_init(&emptyCond,0,0);
}
void putq(MyMSG * msg)
{
sem_wait(&lock);
if(IsQueueFull(queue))
{
nFull++;
sem_post(&lock);
sem_wait(&fullCond);
sem_wait(&lock);
nFull--;
}
EnQueue(queue,msg);
if(nEmpty>0)
{
sem_post(&emptyCond);
}
sem_post(&lock);
}
void getq(MyMSG * msg)
{
sem_wait(&lock);
if(IsQueueEmpty(queue))
{
nEmpty++;
sem_post(&lock);
sem_wait(&emptyCond);
sem_wait(&lock);
nEmpty--;
}
DeQueue(msg);
if(nFull>0)
{
sem_post(&fullCond);
}
sem_post(&lock);
}
void destroyThreadQueue()
{
destroyCircleQueue(queue);
sem_destroy(&lock);
sem_destroy(&fullCond);
sem_destroy(&emptyCond);
}