当多个线程对共享数据同时操作时,为了保证共享数据的一致性,需要同步线程的读写操作。
1互斥量
1.1函数
1创建和销毁互斥量
#include <pthread.h> /*动态创建和销毁*/ int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * attr); int pthread_mutex_destroy(pthread_mutex_t *mutex); 返回值:若成功返回0,否则返回错误编号
/*静态初始化,不需要销毁*/ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; |
2加锁和解锁互斥量
#include <pthread.h> /*加锁,如果互斥量已上锁,调用线程将阻塞直到互斥量被解锁。*/ int pthread_mutex_lock(pthread_mutex_t *mutex); /*解锁,不能解锁由其他线程锁住的互斥量*/ int pthread_mutex_unlock(pthread_mutex_t *mutex); /*尝试加锁,如果互斥量已上锁,调用线程返回失败返回EBUSY而不会阻塞*/ int pthread_mutex_trylock(pthread_mutex_t *mutex); 返回值:若成功返回0,否则返回错误编号 |
1.2应用
/* errors.h*/
#ifndef __errors_h
#define __errors_h
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef DEBUG
#define DPRINTF(arg) printf arg
#else
#define DPRINTF(arg)
#endif
#define err_abort(code,text) do{\
fprintf(stderr, "%s at \"%s\":%d: %s\n",\
text, __FILE__, __LINE__, strerror(code));\
abort();\
}while(0)
#define errno_abort(text) do {\
fprintf(stderr, "%s at \"%s\":%d: %s\n", \
text, __FILE__, __LINE__, strerror (errno)); \
abort();\
}while(0)
#endif
#include "errors.h"
#include <time.h>
#include <pthread.h>
typedef struct alarm_tag
{
struct alarm_tag *link;
time_t time;
int seconds;
char message[64];
}alarm_t;
pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;/*互斥量*/
alarm_t *alarm_list = NULL; /*全局变量,此处为需要同步的共享资源*/
void *alarm_thread(void *arg)
{
alarm_t *alarm;
int sleep_time;
time_t now;
int status;
while (1)
{
/*加锁*/
pthread_mutex_lock(&alarm_mutex);
alarm = alarm_list;
if (alarm == NULL)
{
/*闹钟链表为空,则休眠1s*/
sleep_time = 1;
}
else
{
alarm_list = alarm->link;
now = time(NULL);
if (alarm->time <= now)
{
sleep_time = 0;
}
else
{
sleep_time = alarm->time - now;
}
#ifdef DEBUG
printf("[waiting: %d(%d)\"%s\"]\n", alarm->time, sleep_time, alarm->message);
#endif
}
/*解锁*/
pthread_mutex_unlock(&alarm_mutex);
if (sleep_time > 0)
{
/*开始休眠*/
sleep(sleep_time);
}
else
{
/*将处理器交给另一个等待运行的线程,如果没有就绪的线程则立即返回*/
sched_yield();
}
if (alarm != NULL)
{
printf("(%d) %s\n", alarm->seconds, alarm->message);
free(alarm);
}
}
}
int main(int argc, char *argv[])
{
int status;
char line[128];
alarm_t *alarm, **last, *next;
pthread_t thread;
status = pthread_create(&thread, NULL, alarm_thread, NULL);
if (status != 0)
{
err_abort(status, "Create alarm thread");
}
while (1)
{
printf("Alarm> ");
if (fgets(line, sizeof(line), stdin) == NULL)
exit(0);
if (strlen(line) <= 1)
continue;
alarm = (alarm_t *)malloc(sizeof(alarm_t));
if (alarm == NULL)
{
errno_abort("allocate alarm");
}
if (sscanf(line, "%d %64[^\n]", &alarm->seconds, alarm->message) < 2)
{
fprintf(stderr, "Bad command\n");
free(alarm);
}
else
{
/*加锁*/
pthread_mutex_lock(&alarm_mutex);
alarm->time = time(NULL) + alarm->seconds;
last = &alarm_list;
next = *last;
while (next != NULL)
{
if (next->time >= alarm->time)
{
alarm->link = next;
*last = alarm;
break;
}
last = &next->link;
next = next->link;
}
if (next == NULL)
{
*last = alarm;
alarm->link = NULL;
}
#if DEBUG
printf("[list: ");
for (next = alarm_list; next != NULL; next = next->link)
printf("%d(%d)[\"%s\"]", next->time, next->time - time(NULL), next->message);
printf("]\n");
#endif
/*解锁*/
pthread_mutex_unlock(&alarm_mutex);
}
}
}
2条件变量
2.1函数
1条件变量初始化
#include <pthread.h> int pthread_cond_init(pthread_cond_t * cond, pthread_condattr_t * attr); int pthread_cond_destroy(pthread_cond_t *cond); 返回值:成功返回0,失败返回错误码 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;/*静态初始化*/ |
2等待条件变量
头文件 | #include <pthread.h> |
函数 | int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex); |
参数 | cond: 条件变量 mutex:互斥量,用来同步共享数据。 |
返回值 | 成功返回0,失败返回错误码。等待条件变量总是返回锁住的互斥量。 |
说明 | 在一个条件变量上等待会导致一下原子操作: 释放相关互斥量,等待其他线程发给该条件变量信号或广播该条件变量。当等待条件变量时,互斥量必须始终为释放的,这样其他线程才有机会锁住互斥量,修改条件变量;当线程从条件变量等待中醒来时,它重新继续锁住互斥量。 |
头文件 | #include <pthread.h> |
函数 | int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, struct timespec *timeout); |
参数 | cond:条件变量 mutex:互斥量 timeout:等待的时间值,用秒数tv_sec或分秒数tv_nsec来表示。 struct timespec{ time_t tv_sec;/*秒*/ long tv_nsec;/*纳秒*/ }; timeout这个时间值是绝对数而不是相对数,例如等待3分钟,则需要把当前时间再加上3分钟再转换到timespec结构。 |
返回值 | 成功返回0,失败返回错误码 |
3唤醒等待条件变量的线程
#include <pthread.h> int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); 返回值:成功返回0,失败返回错误码 |
2.2应用
#include "errors.h"
#include <time.h>
#include <pthread.h>
typedef struct alarm_tag
{
struct alarm_tag *link;
time_t time;
int seconds;
char message[64];
}alarm_t;
pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t alarm_cond = PTHREAD_COND_INITIALIZER;
alarm_t *alarm_list = NULL;
time_t current_alarm = 0;
void alarm_insert(alarm_t *alarm)
{
int status;
alarm_t **last, *next;
last = &alarm_list;
next = *last;
while (next != NULL)
{
if (next->time >= alarm->time)
{
alarm->link = next;
*last = alarm;
break;
}
last = &next->link;
next = next->link;
}
if (next == NULL)
{
*last = alarm;
alarm->link = NULL;
}
#if DEBUG
printf("[list: ");
for (next = alarm_list; next != NULL; next = next->link)
printf("%d(%d)[\"%s\"]", next->time, next->time - time(NULL), next->message);
printf("]\n");
#endif
if (current_alarm ==0 || alarm->time < current_alarm)
{
current_alarm = alarm->time;
status = pthread_cond_signal(&alarm_cond);
if (status != 0)
{
err_abort(status, "signal cond");
}
}
}
void *alarm_thread(void *arg)
{
alarm_t *alarm;
struct timespec cond_time;
time_t now;
int status;
int expired;
/*加锁*/
pthread_mutex_lock(&alarm_mutex);
while (1)
{
current_alarm = 0;
while (alarm_list == NULL)
{
/*链表为空,则等待*/
pthread_cond_wait(&alarm_cond, &alarm_mutex);
}
/*摘取alarm链表的第一个元素*/
alarm = alarm_list;
alarm_list = alarm->link;
now = time(NULL);
expired = 0;
if (alarm->time > now)
{
#ifdef DEBUG
printf("[waiting: %d (%d)\"%s\"]\n]", alarm->time, alarm->time - time(NULL), alarm->message);
#endif
cond_time.tv_sec = alarm->time;
cond_time.tv_nsec = 0;
current_alarm = alarm->time;
while (current_alarm == alarm->time)
{
status = pthread_cond_timedwait(&alarm_cond, &alarm_mutex, &cond_time);
if (status == ETIMEDOUT)
{
expired = 1;
break;
}
if (status != 0)
{
err_abort(status, "cond timewait");
}
}
if (!expired)
{
alarm_insert(alarm);
}
}
else
{
expired = 1;
}
if (expired)
{
printf("(%d) %s\n", alarm->seconds, alarm->message);
free(alarm);
}
}
}
int main(int argc, char *argv[])
{
int status;
char line[128];
alarm_t *alarm;
pthread_t thread;
status = pthread_create(&thread, NULL, alarm_thread, NULL);
if (status != 0)
{
err_abort(status, "Create alarm thread");
}
while (1)
{
printf("Alarm> ");
if (fgets(line, sizeof(line), stdin) == NULL)
exit(0);
if (strlen(line) <= 1)
continue;
alarm = (alarm_t *)malloc(sizeof(alarm_t));
if (alarm == NULL)
{
errno_abort("allocate alarm");
}
if (sscanf(line, "%d %64[^\n]", &alarm->seconds, alarm->message) < 2)
{
fprintf(stderr, "Bad command\n");
free(alarm);
}
else
{
pthread_mutex_lock(&alarm_mutex);
alarm->time = time(NULL) + alarm->seconds;
alarm_insert(alarm);
pthread_mutex_unlock(&alarm_mutex);
}
}
}