互斥量
互斥量(Mutex)是“mutual exclusion”的缩写。互斥量是实现线程同步,和保护同时写共享数据的主要方法。
使用互斥量的典型顺序如下:
1. 创建和初始一个互斥量
2. 多个线程尝试去锁定该互斥量
3. 仅有一个线程可以成功锁定改互斥量
4. 锁定成功的线程做一些处理
5. 线程解锁该互斥量
6. 另外一个线程获得互斥量,重复上述过程
7. 最后销毁互斥量
创建和销毁互斥量
pthread_mutex_t_numtex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr);
int pthread_mutex_destory(pthread_mutex_t *mutex);
互斥量必须用类型pthread_mutex_t类型声明,在使用前必须初始化,这里有两种方法可以初始化互斥量:
- 声明时静态地,如:pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
- 动态地用pthread_mutex_init()函数,这种方法允许设定互斥量的属性对象attr。
互斥量初始化后是解锁的。
attr对象用于设置互斥量对象的属性,使用时必须声明为pthread_mutextattr_t类型,默认值可以是NULL。
Pthreads标准定义了三种可选的互斥量属性:
- 协议(Protocol): 指定了协议用于阻止互斥量的优先级改变
- 优先级上限(Prioceiling):指定互斥量的优先级上限
- 进程共享(Process-shared):指定进程共享互斥量
注意所有实现都提供了这三个可先的互斥量属性。
pthread_mutexattr_init()和pthread_mutexattr_destroy()函数分别用于创建和销毁互斥量属性对象。
pthread_mutex_destroy()应该用于释放不需要再使用的互斥量对象。
将互斥量和它要保护的数据明显的关联起来是个不错的选择。如下例:
1 #include<pthread.h>
2 #include "errors.h"
3
4 typedef struct my_struct_tag {
5 pthread_mutex_t mutex;
6 int value;
7 } my_struct_t;
8
9 int main(int argc, char* argv[])
10 {
11 my_struct_t *data;
12 int status;
13
14 data = malloc(sizeof(my_struct_t));
15 if( data == NULL )
16 errno_abort("malloc");
17 status = pthread_mutex_init(&data->mutex, NULL);
18 if( status != 0 )
19 err_abort(status, "init mutex");
20 status = pthread_mutex_destroy(&data->mutex);
21 if( status != 0 )
22 err_abort(status, "destroy mutex");
23 (void)free(data);
24 return status;
25
26 }
mutex_dynamic.c
加锁和解锁互斥量
int pthread_mutex_lock(pthread_mutex_t *mutex);
int phtread_mutex_trylock(pthread_mutex_t *mutex); //非阻塞加锁
int pthrad_mutex_unlock(pthread_mutex_t *mutex);
线程用pthread_mutex_lock()函数去锁定指定的mutex变量,若该mutex已经被另外一个线程锁定了,该调用将会阻塞线程直到mutex被解锁。
pthread_mutex_trylock()尝试着去锁定一个互斥量,然而,若互斥量已被锁定,程序会立刻返回并返回一个忙错误(EBUSY)值。该函数在优先级改变情况下阻止死锁是非常有用的。
线程可以用pthread_mutex_unlock()解锁自己占用的互斥量。在一个线程完成对保护数据的使用,而其它线程要获得互斥量在保护数据上工作时,可以调用该函数。
若有一下情形则会发生错误:
- 互斥量已经被解锁
- 互斥量被另一个线程占用
1 #include<pthread.h>
2 #include<time.h>
3 #include "errors.h"
4
5 typedef struct alarm_tag {
6 struct alarm_tag *link;
7 int seconds;
8 time_t time;
9 char message[64];
10 } alarm_t;
11
12 pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;
13 alarm_t *alarm_list = NULL;
14
15 void *alarm_thread(void *arg)
16 {
17 alarm_t *alarm;
18 int sleep_time;
19 time_t now;
20 int status;
21
22 while(1)
23 {
24 status = pthread_mutex_lock(&alarm_mutex);
25 if( status != 0 )
26 err_abort(status, "Lock mutex");
27 alarm = alarm_list;
28 if( alarm == NULL )
29 sleep_time = 1;
30 else{
31 alarm_list = alarm->link;
32 now = time(NULL);
33 if( alarm->time <= now)
34 sleep_time = 0;
35 else
36 sleep_time = alarm->time - now;
37 #ifdef DEBUG
38 printf("[waiting: %d(%d)\"%s\"]\n", alarm->time,
39 sleep_time, alarm->message);
40 #endif
41 }
42 status = pthread_mutex_unlock(&alarm_mutex);
43 if(status != 0 )
44 err_abort(status, "Unlock mutex");
45 if( sleep_time > 0 )
46 sleep(sleep_time);
47 else
48 sched_yield();
49 if( alarm != NULL)
50 {
51 printf("(%d) %s\n", alarm->seconds, alarm->message);
52 free(alarm);
53 }
54
55
56 }
57 }
58
59 int main(int argc, char *argv[])
60 {
61 int status;
62 char line[128];
63 alarm_t *alarm, **current, *next;
64 pthread_t thread;
65
66 status = pthread_create(&thread, NULL, alarm_thread, NULL);
67 if(status != 0)
68 err_abort(status, "Create alarm thread");
69 while(1)
70 {
71 printf("Alarm> ");
72 if(fgets(line, sizeof(line), stdin) == NULL ) exit(0);
73 if(strlen(line) <= 1) continue;
74 alarm = (alarm_t*)malloc(sizeof(alarm_t));
75 if(alarm == NULL)
76 errno_abort("malloc alarm");
77
78 if(sscanf(line, "%d %64[^\n]", &alarm->seconds, alarm->message) < 2)
79 {
80 fprintf(stderr, "Bad command\n");
81 free(alarm);
82 }
83 else
84 {
85 status = pthread_mutex_lock(&alarm_mutex);
86 if(status != 0)
87 err_abort(status, "mutex lock");
88 alarm->time = time(NULL) + alarm->seconds;
89
90 current = &alarm_list;
91 next = *current;
92 while(next != NULL)
93 {
94 if(next->time >= alarm->time)
95 {
96 alarm->link = next;
97 *current = alarm;
98 break;
99 }
100 current = &next->link;
101 next = next->link;
102 }
103 if(next == NULL)
104 {
105 *current = alarm;
106 alarm->link = NULL;
107 }
108 #ifdef DEBUG
109 printf("[list:");
110 for(next = alarm_list;next != NULL; next = next->link)
111 printf("%d(%d)[\"%s\"] ", next->time,
112 next->time - time(NULL), next->message);
113 printf("]\n");
114 #endif
115 status = pthread_mutex_unlock(&alarm_mutex);
116 if(status != 0)
117 err_abort(status, "Unlock mutex");
118 }
119 }
120 }
alarm_mutex.c
在试锁和回退算法中,总是应该以相反的顺序解锁互斥量:
- 尝试加锁互斥量1;如果成功,再加锁互斥量2;如果成功,再加锁互斥量3。如果某一个互斥量加锁失败,则全部回退。
- 解锁互斥量3/2/1
按照相反顺序解锁,如果第二个线程需要加锁这三个互斥量,则会由于加锁互斥量1失败而回退;而如果先解锁1-2-3这样的顺序,可能会到加锁互斥量3时候才失败,回退代价更大。
1 #include<pthread.h>
2 #include "errors.h"
3
4 #define ITERATIONS 10
5
6 pthread_mutex_t mutex[3] = {
7 PTHREAD_MUTEX_INITIALIZER,
8 PTHREAD_MUTEX_INITIALIZER,
9 PTHREAD_MUTEX_INITIALIZER
10 };
11
12 int backoff = 1;
13 int yield_flag = 0;
14
15 void * lock_forward(void* arg)
16 {
17 int i, iterate, backoffs, status;
18 for( iterate = 0; iterate < ITERATIONS; iterate++ ){
19 backoffs = 0;
20 for( i=0; i< 3; i++ ){
21 if( i == 0 ){
22 status = pthread_mutex_lock(&mutex[i]);
23 if(status != 0)
24 err_abort(status, "First lock");
25 }else{
26 if(backoff)
27 status = pthread_mutex_trylock(&mutex[i]);
28 else
29 status = pthread_mutex_lock(&mutex[i]);
30 if( status == EBUSY) {
31 backoffs++;
32 DPRINTF((
33 " [forward locker"
34 "backing off at %d]\n",
35 i));
36 for(; i>=0 ;i--) {
37 status = pthread_mutex_unlock(&mutex[i]);
38 if(status != 0)
39 err_abort(status, "Backoff");
40 }
41 }else {
42 if( status != 0)
43 err_abort(status, "Lock mutex");
44 DPRINTF((" forward locker got %d \n", i));
45 }
46 }
47 if(yield_flag){
48 if(yield_flag > 0)
49 sched_yield();
50 else
51 sleep(1);
52 }
53 }
54 printf("lock forward got all locks, %d backoffs\n", backoffs);
55 pthread_mutex_unlock(&mutex[2]);
56 pthread_mutex_unlock(&mutex[1]);
57 pthread_mutex_unlock(&mutex[0]);
58 sched_yield();
59 }
60 return NULL;
61 }
62
63 void *lock_backward(void *arg)
64 {
65 int i, iterate, backoffs;
66 int status;
67
68 for ( iterate = 0; iterate < ITERATIONS; iterate++ ) {
69 backoffs = 0;
70 for ( i = 2; i >= 0; i-- ) {
71 if (i == 2 ) {
72 status = pthread_mutex_lock (&mutex[i]);
73 if (status != 0)
74 err_abort(status, "First lock");
75 } else {
76 if (backoff)
77 status = pthread_mutex_trylock(&mutex[i]);
78 else
79 status = pthread_mutex_lock(&mutex[i]);
80 if (status == EBUSY ) {
81 backoffs++;
82 DPRINTF(("[backward locker backing off at %d]\n",i));
83 for (; i < 3; i++) {
84 status = pthread_mutex_unlock(&mutex[i]);
85 if (status != 0)
86 err_abort(status, "Backoff");
87 }
88 } else {
89 if (status != 0)
90 err_abort(status, "Lock mutex");
91 DPRINTF(("backward locker got %d\n", i));
92 }
93 }
94 if (yield_flag) {
95 if (yield_flag > 0)
96 sched_yield();
97 else
98 sleep(1);
99 }
100 }
101 printf("Lock backward got all locks, %d backoffs\n", backoffs);
102 pthread_mutex_unlock(&mutex[0]);
103 pthread_mutex_unlock(&mutex[1]);
104 pthread_mutex_unlock(&mutex[2]);
105 sched_yield();
106 }
107 return NULL;
108 }
109
110 int main(int argc, char* argv[])
111 {
112 pthread_t forward, backward;
113 int status;
114
115 if (argc > 1)
116 backoff = atoi(argv[1]);
117 if (argc > 2)
118 yield_flag = atoi(argv[2]);
119 status = pthread_create(&forward, NULL, lock_forward, NULL);
120 if (status != 0)
121 err_abort(status, "Create forward");
122 status = pthread_create(&backward, NULL, lock_backward, NULL);
123 if (status != 0)
124 err_abort(status, "Create backward");
125 pthread_exit(NULL);
126
127 }
backoff.c