#include<stdio.h>
#include"errors.h"
#include<pthread.h>
#include<time.h>
typedef struct alarm_tag{
int seconds;
time_t alarm_time;
char message[64];
struct alarm_tag *link;
}alarm_t;
alarm_t *alarm_list=NULL;
pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_t=PTHREAD_COND_INITIALIZER;
time_t current_time=0;
void insert(alarm_t *alarm)
{
int status;
alarm_t **last, *next;
last = &alarm_list;
next = *last;
while (next != NULL)
{
if (next->alarm_time >= alarm->alarm_time)
{
alarm->link = next;
*last = alarm;
break;
}
last = &next->link;
next = next->link;
}
if (next == NULL) {
*last = alarm;
alarm->link = NULL;
}
/*注意这里链表的插入方法,标准的链表插入中,需要分情况讨论,队头,队中,队尾,这里把队头和队中进行了合并,利用的是一个**last指针,这个指针在刚开始的时候保存的是队头的地址,如果不需要在队头插入,利用last=&next->link,可以保存下一个的节点的地址,找到合适的插入地址,让last指向alarm,alarm指向next即可。本质上和链表的插入方法是一样的,但是巧妙利用了指针。
刚才仔细考虑才知道自己一直存在的误区是链头指针不是元素,它是指向第一个元素的,每个元素都有一个指针指向它,那么只要让这个指向元素的指针链进行传递就可以了,这也就是上面的代码表示的含义。让last指向这个指针链,但是因为这个指针链有两个名字,alarm_list和next->link,所以找一个再找一个指针来指向它们,这就是**last。*/
if(current_time==0||current_time>alarm->alarm_time)
{
current_time=alarm->alarm_time;
status=pthread_cond_signal(&cond_t);
if(status!=0)
err_abort(status,"Signal cond");
}
}
void *alarm_wait(void *arg)
{
struct timespec cond_time;
alarm_t *alarm;
time_t now;
int status,expiration;
status=pthread_mutex_lock(&mutex);
if(status!=0)
err_abort(status,"mutex lock");
while(1)
{
current_time=0;
while(alarm_list==NULL)
{
status=pthread_cond_wait(&cond_t,&mutex);
if(status!=0)
err_abort(status,"alarm wait");
}
alarm=alarm_list;
alarm_list=alarm->link;//这一句保证了当前正在使用的闹铃已经不在队列当中
now=time(NULL);
cond_time.tv_sec=alarm->alarm_time;
cond_time.tv_nsec=0;
current_time=alarm->alarm_time;
expiration=0;
if(alarm->alarm_time>now)
{
while(current_time==alarm->alarm_time)
{
status=pthread_cond_timedwait(&cond_t,&mutex,&cond_time);
if(status==ETIMEDOUT)
{
expiration=1;
break;
}
if(status!=0)
err_abort(status,"alarm waittime");
}
if(!expiration)
insert(alarm);
}
else expiration=1;
if(expiration)
{
printf("%d,%s/n",alarm->seconds,alarm->message);
free(alarm);//释放空间
}
}
}
int main(){
pthread_t wait;
alarm_t *new_alarm;
int status;
status = pthread_create(&wait,NULL,alarm_wait,NULL);
if(status!=0)
err_abort(status,"create alarm thread");
while(1)
{
printf ("Alarm> ");
new_alarm=(alarm_t*)malloc(sizeof(alarm_t));
scanf("%d %s",&new_alarm->seconds,new_alarm->message);//直接scanf也是可以的,当然这里没有原程序的纠错机制,可以另外加上
new_alarm->alarm_time=time(NULL)+new_alarm->seconds;
status=pthread_mutex_lock(&mutex);//这里的操作是必须的,保证两个线程对链表的操作是互斥的,以防止插入元素过程中,等待线程启动,造成不可知的后果
if(status!=0)
err_abort(status,"mutex lock");
insert(new_alarm);
status=pthread_mutex_unlock(&mutex);
if(status!=0)
err_abort(status,"mutex unlock");
}
}