一个具体的场景:在多线程中,当一个线程获得锁之后异常退出后,应该怎么处理?
方案一 使用锁的robust特性
简单地讲,就是当拥有这个锁的线程挂了后,下一个尝试去获得锁的线程会得到EOWNWERDEAD的返回值,新的拥有者应该再去调用pthread_mutex_consistent_np()来保持锁状态的一致性,并解锁。
直接上代码看示例:
1 /*================================================================ 2 * Copyright (C) 2019 Ltd. All rights reserved. 3 * 4 * File Name :robust_mutex.c 5 * Author :Hamilton 6 * Date :2019-07-30 7 * Descriptor: 8 * 9 ================================================================*/ 10 11 #include <stdlib.h> 12 #include <stdio.h> 13 #include <unistd.h> 14 #include <pthread.h> 15 #include <errno.h> 16 17 #define handle_error_en(en, msg) 18 do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0) 19 20 static pthread_mutex_t mtx; 21 22 static void *original_owner_thread(void *ptr) 23 { 24 printf(" [original owner] Setting lock... "); 25 pthread_mutex_lock(&mtx); 26 printf("[original owner] Locked. Now exiting without unlocking. "); 27 pthread_exit(NULL); 28 } 29 static void *bad_thread(void *ptr) 30 { 31 printf(" [bad owner] Setting lock... "); 32 pthread_mutex_lock(&mtx); 33 printf("[bad owner] Locked. Now exiting without unlocking. "); 34 pthread_exit(NULL); 35 } 36 static void *second_thread(void *ptr) 37 { 38 int i = 5; 39 40 while (i--) 41 { 42 int s = pthread_mutex_lock(&mtx); 43 44 if (s == EOWNERDEAD) 45 { 46 printf(" [second thread] pthread_mutex_lock() returned EOWNERDEAD "); 47 48 printf("[second thread] Now make the mutex consistent "); 49 s = pthread_mutex_consistent(&mtx); 50 if (s != 0) 51 handle_error_en(s, "pthread_mutex_consistent"); 52 53 printf("[second thread] Mutex is now consistent; unlocking "); 54 s = pthread_mutex_unlock(&mtx); 55 if (s != 0) 56 handle_error_en(s, "pthread_mutex_unlock"); 57 58 } 59 else if (s < 0) 60 { 61 printf(" [second thread] pthread_mutex_lock() unexpectedly failed "); 62 handle_error_en(s, "pthread_mutex_lock"); 63 } 64 else 65 { 66 printf(" [second thread] pthread_mutex_lock success. "); 67 printf("do somthing.... "); 68 s = pthread_mutex_unlock(&mtx); 69 if (s != 0) 70 handle_error_en(s, "pthread_mutex_unlock"); 71 } 72 sleep(1); 73 } 74 75 pthread_exit(NULL); 76 } 77 78 int main(int argc, char *argv[]) 79 { 80 pthread_t thr; 81 pthread_mutexattr_t attr; 82 int s; 83 84 pthread_mutexattr_init(&attr); 85 /* initialize the attributes object */ 86 pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST); 87 /* set robustness */ 88 89 pthread_mutex_init(&mtx, &attr); /* initialize the mutex */ 90 91 pthread_create(&thr, NULL, original_owner_thread, NULL); 92 93 sleep(1); 94 pthread_create(&thr, NULL, second_thread, NULL); 95 sleep(1); 96 pthread_create(&thr, NULL, bad_thread, NULL); 97 98 /* "original_owner_thread" should have exited by now */ 99 100 int i = 5; 101 while(i--) 102 { 103 s = pthread_mutex_lock(&mtx); 104 105 if (s == EOWNERDEAD) 106 { 107 printf(" [main thread] pthread_mutex_lock() returned EOWNERDEAD "); 108 109 printf("[main thread] Now make the mutex consistent "); 110 s = pthread_mutex_consistent(&mtx); 111 if (s != 0) 112 handle_error_en(s, "pthread_mutex_consistent"); 113 114 printf("[main thread] Mutex is now consistent; unlocking "); 115 s = pthread_mutex_unlock(&mtx); 116 if (s != 0) 117 handle_error_en(s, "pthread_mutex_unlock"); 118 119 } 120 else if (s < 0) 121 { 122 printf(" [main thread] pthread_mutex_lock() unexpectedly failed "); 123 handle_error_en(s, "pthread_mutex_lock"); 124 } 125 else 126 { 127 printf(" [main thread] pthread_mutex_lock success. "); 128 printf("do somthing.... "); 129 s = pthread_mutex_unlock(&mtx); 130 if (s != 0) 131 handle_error_en(s, "pthread_mutex_unlock"); 132 } 133 134 sleep(1); 135 } 136 exit(EXIT_SUCCESS); 137 }
示例中总共包含四个线程,original_owner_thread() 和 bad_thread() 两个线程获得锁后立马退出不释放,其它两个线程main thread 及 second_thread() 轮流抢占锁,并对锁的异常进行恢复处理,看下打印结果:
是不是很简单,通过设置robust特性,并在每次获取锁时判断锁的异常状态,便能很好的处理锁异常退出的情况。
关于锁的robust特性及consistent设定,可参考以下更多资料:
https://docs.oracle.com/cd/E19455-01/806-5257/6je9h032m/index.html
http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_consistent.html
本文示例改编自:http://manpages.ubuntu.com/manpages/bionic/man3/pthread_mutexattr_setrobust.3.html
后记:再看看进程间有没有类似的机制,但是google并没有找到相关介绍,便想看看pthreas_mutex_lock这套机制在process下工作是否正常,先看示例代码:
1 /*================================================================ 2 * Copyright (C) 2019 Ltd. All rights reserved. 3 * 4 * File Name :robust_mutex.c 5 * Author :Hamilton 6 * Date :2019-07-30 7 * Descriptor: 8 * 9 ================================================================*/ 10 11 #include <stdlib.h> 12 #include <stdio.h> 13 #include <unistd.h> 14 #include <pthread.h> 15 #include <errno.h> 16 #include <sys/mman.h> 17 #include <fcntl.h> 18 #include <sys/types.h> 19 #include <sys/stat.h> 20 21 #define SHM_NAME "fasdfasfasfas" 22 23 24 #define handle_error_en(en, msg) 25 do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0) 26 27 static pthread_mutex_t *mtx; 28 static int fd_shm; 29 30 void shm_mutex_init(pthread_mutex_t **mutex) 31 { 32 pthread_mutexattr_t attr; 33 34 pthread_mutexattr_init(&attr); 35 /* initialize the attributes object */ 36 pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST); 37 /* set robustness */ 38 39 // Get shared memory 40 if ((fd_shm = shm_open (SHM_NAME, O_RDWR | O_CREAT, 0660)) == -1) 41 perror ("shm_open"); 42 43 if (ftruncate (fd_shm, sizeof (pthread_mutex_t)) == -1) 44 perror ("ftruncate"); 45 46 if ((*mutex = mmap (NULL, sizeof (pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED, 47 fd_shm, 0)) == MAP_FAILED) 48 perror ("mmap"); 49 50 pthread_mutex_init(*mutex, &attr); /* initialize the mutex */ 51 } 52 int main(int argc, char *argv[]) 53 { 54 int s; 55 56 shm_mutex_init(&mtx); 57 58 if ((s = fork()) < 0) 59 { 60 perror("fork."); 61 } 62 else if (s == 0) // child 63 { 64 sleep(1); 65 printf(" [bad owner] Setting lock... "); 66 pthread_mutex_lock(mtx); 67 printf("[bad owner] Locked. Now exiting without unlocking. "); 68 } 69 else 70 { 71 int i = 5; 72 while(i--) 73 { 74 s = pthread_mutex_lock(mtx); 75 76 if (s == EOWNERDEAD) 77 { 78 printf(" [main thread] pthread_mutex_lock() returned EOWNERDEAD "); 79 80 printf("[main thread] Now make the mutex consistent "); 81 s = pthread_mutex_consistent(mtx); 82 if (s != 0) 83 handle_error_en(s, "pthread_mutex_consistent"); 84 85 printf("[main thread] Mutex is now consistent; unlocking "); 86 s = pthread_mutex_unlock(mtx); 87 if (s != 0) 88 handle_error_en(s, "pthread_mutex_unlock"); 89 90 } 91 else if (s < 0) 92 { 93 printf(" [main thread] pthread_mutex_lock() unexpectedly failed "); 94 handle_error_en(s, "pthread_mutex_lock"); 95 } 96 else 97 { 98 printf(" [main thread] pthread_mutex_lock success. "); 99 printf("do somthing.... "); 100 s = pthread_mutex_unlock(mtx); 101 if (s != 0) 102 handle_error_en(s, "pthread_mutex_unlock"); 103 } 104 105 sleep(1); 106 } 107 } 108 exit(EXIT_SUCCESS); 109 }
编译执行下看看:
进程间通信的锁得放在共享内存中,编译运行OK,也能正常工作。