这系列文章是用管道、消息队列、条件变量来模拟实现信号量的功能。如果有bug,还望各位读者不吝指出。
这里实现的信号量都是只能用于一个进程内多个线程进行通信的,并不能用于进程间的通信。这里的所有函数的返回值同POSIX标准中的线程那些函数一样,如果函数执行成功,返回0,否则返回错误编号。不会设置errno变量。
测试的代码是《UNIX网络编程 卷2:进程间通信》中10.10和10.11节的结合,即多个生产者、多个消费者,多个缓冲区。为了测试方便,我还写了一个线程结构体。
这里先贴出线程代码。
Thread.hpp文件
#ifndef THREAD_HPP
#define THREAD_HPP
#include<pthread.h>
#include<semaphore.h>
typedef void* (threadFun)(void*);
typedef struct Thread_tag
{
pthread_t tid;
sem_t sem;
pthread_mutex_t mutex;
void* parameter; //线程执行函数的参数
threadFun* realFun; //线程的执行函数
int isRunning; //判断线程是否还在运行
int valid; //用于判断这个结构体有没有被初始化,将赋值一个魔数
}Thread_t;
void* thread_temp_fun(void* arg);
int thread_init(Thread_t* thread);
int thread_create(Thread_t* thread, pthread_attr_t *attr, threadFun* fun );
//创建一个线程的时候并不会马上运行线程函数,而是要等到调用thread_start函数
//这类似于Java中的线程,需要显示调用start方法才会运行线程。
int thread_start(Thread_t* thread, void* arg);
int thread_join(Thread_t* thread, void **value);
int thread_cancel(Thread_t* thread);
int thread_destroy(Thread_t* thread);
#endif // THREAD_HPP
Thread.cpp 文件
#include"Thread.hpp"
#include<errno.h>
#define THREAD_VALID 0xfabc
//临时函数。当一个调用thread_create创建一个线程时,线程将运行这个函数
//然后在这个函数里面停留,休眠。等待用户调用thread_start函数将线程唤醒
//然后线程进入到用户提供的线程运行函数。
//正是通过这种方式,造成一种假象:线程在调用thread_create后,并不会马上运行
//直到调用了thread_start函数后才会运行
void* thread_temp_fun(void* arg)
{
void* status;
Thread_t* thread = (Thread_t*)arg;
sem_wait(&thread->sem); //等待另外一个线程调用thread_start唤醒
thread->isRunning = 1;
status = ((thread->realFun))(thread->parameter); //调用线程的执行函数
thread->isRunning = 0;
return status;
}
//这个函数不会线程安全的。
int thread_init(Thread_t* thread)
{
int status;
if( thread == NULL )
return EINVAL;
status = sem_init(&thread->sem, 0, 0);
if( status != 0 )
return status;
status = pthread_mutex_init(&thread->mutex, NULL);
if( status != 0 )
return status;
thread->isRunning = 0;
thread->valid = THREAD_VALID; //赋值一个魔数
return 0;
}
//make this process Atomically
int thread_create(Thread_t* thread, pthread_attr_t* attr, threadFun *fun )
{
int status;
if( thread == NULL || thread->valid != THREAD_VALID ||fun == NULL )
return EINVAL;
status = pthread_mutex_lock(&thread->mutex);
if( status != 0 )
return status;
thread->realFun = fun;
status = pthread_create(&thread->tid, attr, thread_temp_fun, thread);
//ignore the error. if status == 0 and unlock return not 0.
//we cann't return this message to user. it will confuse the user
//the thread's creation is sucessful, but return error code
pthread_mutex_unlock(&thread->mutex);
return status;
}
int thread_start(Thread_t* thread, void *arg)
{
int status;
if( thread == NULL || thread->valid != THREAD_VALID)
return EINVAL;
status = pthread_mutex_lock(&thread->mutex);
if( status != 0 )
return status;
if( thread->isRunning )//now, this thread is running
{
pthread_mutex_unlock(&thread->mutex);
return EBUSY;
}
//将线程运行时的参数传递给线程执行函数
thread->parameter = arg;
status = sem_post(&thread->sem);
return status;
}
int thread_join(Thread_t* thread, void** value)
{
if( thread == NULL || thread->valid != THREAD_VALID)
return EINVAL;
return pthread_join(thread->tid, value);
}
int thread_cancel(Thread_t* thread)
{
if( thread == NULL || thread->valid != THREAD_VALID )
return EINVAL;
return pthread_cancel(thread->tid);
}
int thread_destroy(Thread_t* thread)
{
int status1, status2;
if( thread == NULL || thread->valid != THREAD_VALID)
return EINVAL;
status1 = pthread_mutex_lock(&thread->mutex);
if( status1 != 0 )
return status1;
if( thread->isRunning )
{
pthread_mutex_unlock(&thread->mutex);
return EBUSY;
}
status1 = pthread_mutex_unlock(&thread->mutex);
if( status1 != 0 )
return status1;
status1 = pthread_mutex_destroy(&thread->mutex);
status2 = sem_destroy(&thread->sem);
if( status1 != 0 )
return status1;
return status2;
}