#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");
}
}