首页 > C/C++ > 线程 > Linux多线程编程(四)互斥锁
2016
11-14

Linux多线程编程(四)互斥锁

四、 互斥锁

  1、 基本概念

  2、 API

    1)、 初始化互斥锁

    2)、 销毁互斥锁

    3)、 使互斥保持一致

    4)、 锁定/尝试锁定(获取)互斥锁

    5)、 解除互斥锁锁定(释放互斥锁)

    6)、 初始化互斥锁属性对象

    7)、 销毁互斥锁属性对象

    8)、 设置/获取互斥锁的作用域属性

    9)、 设置/获取互斥锁的类型属性

    10)、 设置/获取互斥锁的协议属性

    11)、 设置/获取互斥锁属性对象的优先级上限属性

    12)、 设置/获取互斥锁对象的优先级上限属性

    13)、 设置/获取互斥锁的强健属性

 

 

四、互斥锁

1、基本概念

互斥(mutex):顾名思义即是相互排斥的锁,它是最基本的同步形式,用于保护临界区,以保证任何时刻都只有一个线程在执行临界区中的代码。在任意时刻都只有一个线程能持有这把锁,当已经有线程拿到这把锁的时候,其它请求获得这把锁的线程将会被阻塞直到持有锁的线程释放了这把锁或者得到一个EBUSY的错误。
临界区的基本形式为:

mutex_lock(…);
临界区
mutex_unlock(…);

即位于lock之后,unlock之前的部分是临界区。
互斥锁用来解决互斥问题非常合适,因为它可以保证同一时刻只有一个线程能拿到这把锁,进而执行其临界区中的代码。最常见互斥场景是:多个线程会访问它们共享的资源,为了保证资源的一致性,因而需要进行互斥访问,以保证任何时刻都只有一个线程在访问该资源。因而大部分场景下临界区中放的都是操作互斥资源的代码。
互斥锁解决同步问题就不是那么合适了,因为需要同步的线程之间往往有依赖或者顺序关系,但是互斥锁自己无法保证这个顺序。
如果有多个线程在等待一个互斥锁,则在持有互斥锁的线程释放锁后锁将被等待锁的线程中具有最高优先级的那个获得,如果最高优先级线程有多个,则这些线程中谁将获得锁是不确定的。

1、API

POSIX定义的互斥锁的数据类型是: pthread_mutex_t
相关API

  1. #include <pthread.h>  
  2. int pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t *mattr); 成功返回0,其它返回值表示出错  
  3. int pthread_mutex_consistent_np(pthread_mutex_t *mutex); 成功返回0,其它返回值表示出错  
  4. int pthread_mutex_lock(pthread_mutex_t *mutex); 成功返回0,其它返回值表示出错  
  5. int pthread_mutex_unlock(pthread_mutex_t *mutex); 成功返回0,其它返回值表示出错  
  6. int pthread_mutex_trylock(pthread_mutex_t *mutex); 成功返回0,其它返回值表示出错  
  7. int pthread_mutex_destroy(pthread_mutex_t *mp); 成功返回0,其它返回值表示出错  
  8. int pthread_mutexattr_init(pthread_mutexattr_t *mattr);成功返回0,其它返回值表示出错  
  9. int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr);成功返回0,其它返回值表示出错  
  10. int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr,int pshared);成功返回0,其它返回值表示出错  
  11. int pthread_mutexattr_getpshared(pthread_mutexattr_t *mattr,int *pshared);成功返回0,其它返回值表示出错  
  12. int pthread_mutexattr_settype(pthread_mutexattr_t *attr , int type);成功返回0,其它返回值表示出错  
  13. int pthread_mutexattr_gettype(pthread_mutexattr_t *attr , int *type);成功返回0,其它返回值表示出错  
  14. int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol);成功返回0,其它返回值表示出错  
  15. int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, int *protocol);成功返回0,其它返回值表示出错  
  16. int pthread_mutexattr_setprioceiling(pthread_mutexatt_t *attr, int prioceiling, int *oldceiling);成功返回0,其它返回值表示出错  
  17. int pthread_mutexattr_getprioceiling(const pthread_mutexatt_t *attr, int *prioceiling);成功返回0,其它返回值表示出错  
  18. int pthread_mutex_setprioceiling(pthread_mutex_t *mutex, int prioceiling, int *old_ceiling);成功返回0,其它返回值表示出错  
  19. int pthread_mutex_getprioceiling(const pthread_mutex_t *mutex, int *prioceiling);成功返回0,其它返回值表示出错  
  20. int pthread_mutexattr_setrobust_np(pthread_mutexattr_t *attr, int *robustness);成功返回0,其它返回值表示出错  
  21. int pthread_mutexattr_getrobust_np(const pthread_mutexattr_t *attr, int *robustness);成功返回0,其它返回值表示出错  

1)、初始化互斥锁

  1. #include <pthread.h>  
  2. int pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t *mattr); 成功返回0,其它返回值表示出错  

 

如果互斥锁变量是静态的则可以直接用PTHREAD_MUTEX_INITIALIZER来初始化它,比如:
static pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER
如果互斥锁变量是动态分配的,则必须在使用它之前用pthread_mutex_init来初始化它.如果动态地分配互斥量,那么释放内存前需要调用pthread_mutex_destroy.
pthread_mutex_init用于初始化互斥锁,如果mattr为NULL则用缺省值初始化由mp所指向的互斥锁,否则使用指定的mattr初始化互斥锁。
使用PTHREAD_MUTEX_INITIALIZER与动态分配具有null 属性的 pthread_mutex_init等效,不同之处在于PTHREAD_MUTEX_INITIALIZER 宏不进行错误检查。
如果使用pthread_mutex_init初始化互斥锁,并且指定的mattr具有PTHREAD_MUTEX_ROBUST_NP属性,则互斥锁所使用的内存必须在调用pthread_mutex_init之前被清0.
在有线程正在使用互斥锁时,不能重新初始化互斥锁或销毁它。

 

2)、销毁互斥锁

  1. #include <pthread.h>  
  2. int pthread_mutex_destroy(pthread_mutex_t *mp); 成功返回0,其它返回值表示出错   

 

pthread_mutex_destroy可以销毁与指定的互斥锁相关联的任何状态。

 

3)、使互斥保持一致

  1. #include <pthread.h>  
  2. int pthread_mutex_consistent_np(pthread_mutex_t *mutex); 成功返回0,其它返回值表示出错  

 

如果一个互斥锁的持有者没有释放该锁退出了,则在默认情况下当其它线程再去获取这个锁的时候,就会阻塞从而造成死锁。可以更改互斥锁的属性来改变这种默认的方式:
  pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT);
  pthread_mutexattr_setrobust_np(&mattr,PTHREAD_MUTEX_ROBUST_NP);
通过设置锁的上面两个属性,互斥锁就不再具有默认的行为,当一个锁的owner死掉后,其它线程再去获取这个锁的时候,不会被阻塞,而是会获得这个锁,但是同时会得到一个EOWNERDEAD的错误。
然后获得锁的线程可以尝试处理这个错误:

  1. 首先调用pthread_mutex_consistent_np函数来恢复该锁的一致性,
  2. 然后调用pthread_mutex_unlock来解锁
  3. 接下来在调用加锁

这样该锁的行为就恢复正常了。
如果pthread_mutex_consistent_np在恢复锁的一致性时候没有成功,步骤c就不能再执行了,锁也不能被使用了,而且接下来的线程在获取锁都无法获得该锁,而是只能得到返回值ENOTRECOVERABLE。
如果获取某个锁的时候得到了ENOTRECOVERABLE的错误,就意味这这个锁不能被使用了,此时只能调用pthread_mutex_destroy销毁互斥锁然后再调用pthread_mutex_int重新初始化该互斥锁,之后才能再使用该互斥锁。

4)、锁定/尝试锁定(获取)互斥锁

  1. #include <pthread.h>  
  2. int pthread_mutex_lock(pthread_mutex_t *mutex); 成功返回0,其它返回值表示出错  
  3. int pthread_mutex_trylock(pthread_mutex_t *mutex); 成功返回0,其它返回值表示出错  

 

pthread_mutex_lock可以锁定指定的互斥锁。
当它返回时,该互斥锁已被锁定,调用它的线程就获得了这个互斥锁。如果该互斥锁已被另一个线程锁定和拥有,则调用线程将阻塞,直到该互斥锁变为可用为止。
互斥锁的类型不同,pthread_mutex_lock的行为也有所不同。

 

pthread_mutex_trylock可以尝试锁定指定的互斥锁。
pthread_mutex_trylock是 pthread_mutex_lock的非阻塞版本。如果 mutex 所引用的互斥对象当前被任何线程锁定,则将立即返回该调用。否则,该互斥锁将被锁定,调用线程成为其持有者。

 

5)、解除互斥锁锁定(释放互斥锁)

  1. #include <pthread.h>  
  2. int pthread_mutex_unlock(pthread_mutex_t *mutex); 成功返回0,其它返回值表示出错  

 

pthread_mutex_unlock可以解除指定互斥锁的锁定即释放互斥锁。

 

 

对互斥量进行加锁,需要调用pthread_mutex_lock,如果互斥量已经上锁,调用线程阻塞直至互斥量解锁。对互斥量解锁,需要调用pthread_mutex_unlock.

如果线程不希望被阻塞,他可以使用pthread_mutex_trylock尝试对互斥量进行加锁。如果调用pthread_mutex_trylock时互斥量处于未锁住状态,那么pthread_mutex_trylock将锁住互斥量,否则就会失败,不能锁住互斥量,而返回EBUSY。该函数在优先级改变情况下阻止死锁是非常有用的。

线程可以用pthread_mutex_unlock()解锁自己占用的互斥量。在一个线程完成对保护数据的使用,而其它线程要获得互斥量在保护数据上工作时,可以调用该函数。若有一下情形则会发生错误:

    互斥量已经被解锁

    互斥量被另一个线程占用

互斥量并没有多么“神奇”的,实际上,它们就是参与的线程的“君子约定”。写代码时要确信正确地锁定,解锁互斥量。下面演示了一种逻辑错误:

·                    Thread 1     Thread 2     Thread 3

·                    Lock         Lock          

·                    A = 2        A = A+1      A = A*B

·                    Unlock       Unlock     

 

Q:有多个线程等待同一个锁定的互斥量,当互斥量被解锁后,那个线程会第一个锁定互斥量?

A:除非线程使用了优先级调度机制,否则,线程会被系统调度器去分配,那个线程会第一个锁定互斥量是随机的。

 

 

实例1:

/*无锁*/

  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <stdio.h>
  4. #include <unistd.h>
  5.  
  6. int run_now=1;   /*用run_now代表共享资源*/
  7.  
  8. void *thread_function(void *arg)
  9. {
  10.     int print_count2=0;
  11.  
  12.     while(print_count2++<5)
  13.     {
  14.         if(run_now==2)  /*子线程:如果run_now为1就把它修改为1*/
  15.         {
  16.             printf("function thread is run\n");
  17.             run_now=1;
  18.         }
  19.         else
  20.         {
  21.             printf("function thread is sleep\n");
  22.             sleep(1);
  23.         }
  24.     }
  25.     pthread_exit(NULL);
  26. }
  27.  
  28. int main()
  29. {
  30.     int print_count1=0; /*用于控制循环*/
  31.     pthread_t a_thread;
  32.  
  33.     if(pthread_create(&a_thread,NULL,thread_function,NULL)!=0)   /*创建一个进程*/
  34.     {
  35.         perror("Thread createion failed");
  36.         exit(1);
  37.     }
  38.  
  39.     while(print_count1++<5)
  40.     {
  41.         if(run_now==1)  /*主线程:如果run_now为1就把它修改为2*/
  42.         {
  43.             printf("main thread is run\n");
  44.             run_now=2;
  45.         }
  46.         else
  47.         {
  48.             printf("main thread is sleep\n");
  49.             sleep(1);
  50.         }
  51.     }
  52.     pthread_join(a_thread,NULL); /*等待子线程结束*/
  53.     exit(0);
  54. }

 

 

 

运行结果为

  1.  vteyga@root:/root>./pthread_nolock
  2. main thread is run
  3. main thread is sleep
  4. function thread is run
  5. function thread is sleep
  6. main thread is run
  7. main thread is sleep
  8. function thread is run
  9. function thread is sleep
  10. main thread is run
  11. function thread is run

 

我们可以看到main线程和function线程是交替运行的。它们都可以对run_now进行操作 

 

 

实例2:

/*加锁*/

  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <stdio.h>
  4.  
  5. int run_now=1;  /*用run_now代表共享资源*/
  6. pthread_mutex_t work_mutex;  /*定义互斥量*/
  7.  
  8. void *thread_function(void *arg)
  9. {
  10.     int print_count2=0;
  11.  
  12.     sleep(1);
  13.     if(pthread_mutex_lock(&work_mutex)!=0)
  14.     {
  15.         perror("Lock failed");
  16.         exit(1);
  17.     }
  18.     else
  19.         printf("function lock\n");
  20.  
  21.     while(print_count2++<5)
  22.     {
  23.         if(run_now==2)  /*分进程:如果run_now为1就把它修改为1*/
  24.         {
  25.             printf("function thread is run\n");
  26.             run_now=1;
  27.         }
  28.         else
  29.         {
  30.             printf("function thread is sleep\n");
  31.             sleep(1);
  32.         }
  33.     }
  34.  
  35.     if(pthread_mutex_unlock(&work_mutex)!=0)  /*对互斥量解锁*/
  36.     {
  37.         perror("unlock failed");
  38.         exit(1);
  39.     }
  40.     else
  41.         printf("function unlock\n");
  42.  
  43.     pthread_exit(NULL);
  44. }
  45.  
  46. int main()
  47. {
  48.     int res;
  49.     int print_count1=0;
  50.     pthread_t a_thread;
  51.  
  52.     if(pthread_mutex_init(&work_mutex,NULL)!=0)  /*初始化互斥量*/
  53.     {
  54.         perror("Mutex init faied");
  55.         exit(1);
  56.     }
  57.  
  58.     if(pthread_create(&a_thread,NULL,thread_function,NULL)!=0)  /*创建新线程*/
  59.     {
  60.         perror("Thread createion failed");
  61.         exit(1);
  62.     }
  63.  
  64.     if(pthread_mutex_lock(&work_mutex)!=0)  /*对互斥量加锁*/
  65.     {
  66.         perror("Lock failed");
  67.         exit(1);
  68.     }
  69.     else
  70.         printf("main lock\n");
  71.  
  72.     while(print_count1++<5)
  73.     {
  74.         if(run_now==1)  /*主线程:如果run_now为1就把它修改为2*/
  75.         {
  76.             printf("main thread is run\n");
  77.             run_now=2;
  78.         }
  79.         else
  80.         {
  81.             printf("main thread is sleep\n");
  82.             sleep(1);
  83.         }
  84.     }
  85.  
  86.     if(pthread_mutex_unlock(&work_mutex)!=0) /*对互斥量解锁*/
  87.     {
  88.         perror("unlock failed");
  89.         exit(1);
  90.     }
  91.     else
  92.         printf("main unlock\n");
  93.  
  94.     pthread_mutex_destroy(&work_mutex); /*收回互斥量资源*/
  95.     pthread_join(a_thread,NULL); /*等待子线程结束*/
  96.     exit(0);
  97. }

 

 

 

运行结果为

  1.  vteyga@root:/root>./pthread_lock
  2. main lock
  3. main thread is run
  4. main thread is sleep
  5. main thread is sleep
  6. main thread is sleep
  7. main thread is sleep
  8. main unlock
  9. function lock
  10. function thread is run
  11. function thread is sleep
  12. function thread is sleep
  13. function thread is sleep
  14. function thread is sleep
  15. unlock failed: Success

 

 

我们从运行结果可以看到,当主进程把互斥量锁住后,子进程就不能对共享资源进行操作了

 

6)、初始化互斥锁属性对象

  1. #include <pthread.h>  
  2. int pthread_mutexattr_init(pthread_mutexattr_t *mattr);成功返回0,其它返回值表示出错  
  3. int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr);成功返回0,其它返回值表示出错

 

互斥锁具有一些属性,通过修改这些属性可以控制锁的一些行为。缺省的互斥锁属性及其值如下:

  1. pshared:          PTHREAD_PROCESS_PRIVATE
  2. type:                  PTHREAD_MUTEX_DEFAULT
  3. protocol:           PTHREAD_PRIO_NONE
  4. prioceiling:       – 
  5. robustness:    PTHREAD_MUTEX_STALLED_NP

可以用pthread_mutexattr_init将与互斥锁对象相关联的属性初始化为其缺省值。pthread_mutexattr_init的参数类型实际上是opaque的,其中包含一个由系统分配的属性对象。该函数执行过程中会为属性对象分配所需的内存,因而如果未通过pthread_mutexattr_destroy销毁互斥锁属性对象时就会导致内存泄漏。


对于互斥锁属性对象,必须首先通过调用pthread_mutexattr_destroy将其销毁,才能重新初始化该对象。

7)、销毁互斥锁属性对象

  1. #include <pthread.h>  
  2. int pthread_mutex_destroy(pthread_mutex_t *mp); 成功返回0,其它返回值表示出错  

 

pthread_mutexattr_destroy销毁指定的互斥锁属性对象,实际上它完成了释放由pthread_mutexattr_init分配的内存的过程。

 

 

互斥量属性分为共享互斥量属性类型互斥量属性。两种属性分别由不同的函数得到并由不同的函数进行修改。pthread_mutexattr_getpshared和pthread_mutexattr_setpshared函数可以获得和修改共享互斥量属性。pthread_mutexattr_gettype和pthread_mutexattr_settype函数可以获得和修改类型互斥量属性。下面我们分别介绍

8)、设置/获取互斥锁的作用域属性

  1. #include <pthread.h>   
  2. int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr,int pshared);成功返回0,其它返回值表示出错  
  3. int pthread_mutexattr_getpshared(pthread_mutexattr_t *mattr,int *pshared);成功返回0,其它返回值表示出错

 

函数pthread_mutexattr_setpshared用来设置互斥锁的作用域。
互斥锁变量可以是进程专用的变量,也可以是跨越进程边界的变量。
范围属性的取值及其含义:

  1. PTHREAD_PROCESS_SHARED:具有该属性的互斥锁可以在多个进程中的线程之间共享。
  2. PTHREAD_PROCESS_PRIVATE:只有创建本互斥锁的线程所在的进程内的线程才能够使用该互斥锁变量。该值是缺省值。

函数pthread_mutexattr_getpshared可用来返回由 pthread_mutexattr_setpshared设置的互斥锁变量的范围。

9)、设置/获取互斥锁的类型属性

  1. #include <pthread.h>  
  2. int pthread_mutexattr_settype(pthread_mutexattr_t *attr , int type);成功返回0,其它返回值表示出错  
  3. int pthread_mutexattr_gettype(pthread_mutexattr_t *attr , int *type);成功返回0,其它返回值表示出错  

 

pthread_mutexattr_settype用来设置指定互斥锁的类型属性。类型属性的缺省值为 PTHREAD_MUTEX_DEFAULT。
互斥锁的类型及其行为:

  1. PTHREAD_MUTEX_NORMAL:不提供死锁检测。如果一个线程试图对一个互斥锁重复锁定,将会引起这个线程的死锁。如果试图解锁一个由别的线程锁定的互斥锁会引发不可预料的结果。如果一个线程试图解锁已经被解锁的互斥锁也会引发不可预料的结果
  2. PTHREAD_MUTEX_ERRORCHECK:提供错误检查。如果一个线程试图对一个互斥锁重复锁定,将会返回一个错误代码。如果试图解锁一个由别的线程锁定的互斥锁将会返回一个错误代码。如果一个线程试图解锁已经被解锁的互斥锁也将会返回一个错误代码
  3. PTHREAD_MUTEX_RECURSIVE:如果一个线程对这种类型的互斥锁重复上锁,不会引起死锁,一个线程对这类互斥锁的多次重复上锁必须由这个线程来重复相同数量的解锁,这样才能解开这个互斥锁,别的线程才能得到这个互斥锁。如果试图解锁一个由别的线程锁定的互斥锁将会返回一个错误代码。如果一个线程试图解锁已经被解锁的互斥锁也将会返回一个错误代码。这种类型的互斥锁只能是进程私有的(作用域属性为PTHREAD_PROCESS_PRIVATE)
  4. PTHREAD_MUTEX_DEFAULT:这种类型的互斥锁不会自动检测死锁。如果一个线程试图对一个互斥锁重复锁定,将会引起不可预料的结果。如果试图解锁一个由别的线程锁定的互斥锁会引发不可预料的结果。如果一个线程试图解锁已经被解锁的互斥锁也会引发不可预料的结果。POSIX标准规定,对于某一具体的实现,可以把这种类型的互斥锁定义为其他类型的互斥锁

在linux中互斥锁的相关类型定义如下(最好的办法是检查pthread.h这个头文件):
  #if defined __USE_UNIX98 || defined __USE_XOPEN2K8
  ,
  PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_TIMED_NP,
  PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,
  PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,
  PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL
  #endif
pthread_mutexattr_gettype用来获取由pthread_mutexattr_settype设置的互斥锁的类型属性。

10)、设置/获取互斥锁的协议属性

  1. #include <pthread.h>  
  2. int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol);成功返回0,其它返回值表示出错  
  3. int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, int *protocol);成功返回0,其它返回值表示出错   

 

pthread_mutexattr_setprotocol用来设置互斥锁的协议属性。
互斥锁协议属性的可能值及其含义:

  1. PTHREAD_PRIO_NONE:    线程的优先级和调度不会受到互斥锁拥有权的影响.
  2. PTHREAD_PRIO_INHERIT: 此协议值会影响拥有该互斥锁的线程的优先级和调度。如果更高优先级的线程因thrd1所拥有的一个或多个互斥锁而被阻塞,而这些互斥锁是用 PTHREAD_PRIO_INHERIT 初始化的,则thrd1的运行优先级为优先级pri1和优先级pri2中优先级较高的那一个,其中

    • thrd1的优先级为pri1
    • 所有正在等待这些互斥锁(这些互斥锁是 thrd1指所拥有的互斥锁)的线程的最高优先级为pri2

如果thrd1因另一个线程(thrd3) 拥有的互斥锁而被阻塞,则相同的优先级继承效应会以递归方式传播给thrd3。
使用PTHREAD_PRIO_INHERIT可以避免优先级逆转。当低优先级的线程持有较高优先级线程所需的锁时,就会发生优先级逆转。此时只有在较低优先级的线程释放该锁之后,较高优先级的线程才能继续执行。
如果没有优先级继承,底优先级的线程可能会在很长一段时间内都得不到调度,而这会导致等待低优先级线程锁持有的锁的高优先级线程也等待很长时间(因为低优先级线程无法运行,因而就无法释放锁,所以高优先级线程只能继续阻塞在锁上)。使用优先级继承可以短时间的提高低优先级线程的优先级,从而使它可以尽快得到调度,然后释放锁。低优先级线程在释放锁后就会恢复自己的优先级。

  1. PTHREAD_PRIO_PROTECT:当线程拥有一个或多个使用PTHREAD_PRIO_PROTECT 初始化的互斥锁时,线程的优先级和调度会受到影响。线程将以优先级pri1和优先级pri2中优先级较高的那一个优先级来运行,其中

    • 线程的优先级为pri1
    • 所有被线程持有的锁的最高优先级为pri2

被该线程所持有的锁阻塞的高优先级线程对该线程的调度没有影响。

PTHREAD_PRIO_INHERIT 和 PTHREAD_PRIO_PROTECT 只有在采用实时调度策略SCHED_FIFO 或 SCHED_RR的优先级进程内可用。(The PTHREAD_PRIO_INHERIT and PTHREAD_PRIO_PROTECT mutex attributes are usable only by privileged processes running in the realtime (RT) scheduling class SCHED_FIFO or SCHED_RR.)
一个线程可以同时拥有多个混合使用 PTHREAD_PRIO_INHERIT 和 PTHREAD_PRIO_PROTECT协议属性初始化的互斥锁。在这种情况下,该线程将以通过其中任一协议获取的最高优先级执行。
pthread_mutexattr_getprotocol可用来获取互斥锁属性对象的协议属性。

11)、设置/获取互斥锁属性对象的优先级上限属性

  1. #include <pthread.h>  
  2. int pthread_mutexattr_setprioceiling(pthread_mutexatt_t *attr, int prioceiling, int *oldceiling);成功返回0,其它返回值表示出错  
  3. int pthread_mutexattr_getprioceiling(const pthread_mutexatt_t *attr, int *prioceiling);成功返回0,其它返回值表示出错

 

pthread_mutexattr_setprioceiling用来设置指定互斥锁属性对象的优先级上限属性。
prioceiling指定已初始化互斥锁的优先级上限。优先级上限定义执行互斥锁保护的临界段时的最低优先级。prioceiling 位于 SCHED_FIFO 所定义的优先级的最大范围内。要避免优先级倒置,请将 prioceiling 设置为高于或等于可能会锁定特定互斥锁的所有线程的最高优先级。
oldceiling 用于返回以前的优先级上限值。
pthread_mutexattr_getprioceiling用来获取互斥锁属性对象的优先级上限属性。

12)、设置/获取互斥锁对象的优先级上限属性

  1. #include <pthread.h>  
  2. int pthread_mutex_setprioceiling(pthread_mutex_t *mutex, int prioceiling, int *old_ceiling);成功返回0,其它返回值表示出错  
  3. int pthread_mutex_getprioceiling(const pthread_mutex_t *mutex, int *prioceiling);成功返回0,其它返回值表示出错

 

pthread_mutex_setprioceiling可更改互斥锁 mutex 的优先级上限 prioceiling。
pthread_mutex_setprioceiling可锁定互斥锁(如果未锁定的话),或者一直处于阻塞状态,直到它成功锁定该互斥锁,更改该互斥锁的优先级上限并将该互斥锁释放为止。锁定互斥锁的过程无需遵循优先级保护协议。
如果 pthread_mutex_setprioceiling成功,则将在 old_ceiling 中返回以前的优先级上限值。如果pthread_mutex_setprioceiling失败,则互斥锁的优先级上限保持不变。
pthread_mutex_getprioceiling会返回 mutex 的优先级上限 prioceiling。

13)、设置/获取互斥锁的强健属性

  1. #include <pthread.h>  
  2. int pthread_mutexattr_setrobust_np(pthread_mutexattr_t *attr, int *robustness);成功返回0,其它返回值表示出错 
  3. int pthread_mutexattr_getrobust_np(const pthread_mutexattr_t *attr, int *robustness);成功返回0,其它返回值表示出错

 

pthread_mutexattr_setrobust_np用来设置互斥锁属性对象的强健属性。仅当定义了符号 _POSIX_THREAD_PRIO_INHERIT 时,pthread_mutexattr_setrobust_np()才适用。
robustness 定义在互斥锁的持有者“死亡”时的行为。pthread.h 中定义的 robustness 的值为PTHREAD_MUTEX_ROBUST_NP 或 PTHREAD_MUTEX_STALLED_NP。缺省值为PTHREAD_MUTEX_STALLED_NP。

  1. PTHREAD_MUTEX_STALLED_NP: 如果互斥锁的持有者死亡,则以后对 pthread_mutex_lock() 的所有调用将以不确定的方式被阻塞。
  2. PTHREAD_MUTEX_ROBUST_NP: 如果互斥锁的持有者“死亡”了,或者持有这样的互斥锁的进程unmap了互斥锁所在的共享内存或者持有这样的互斥锁的进程执行了exec调用,则会解除锁定该互斥锁。互斥锁的下一个持有者将获取该互斥锁,并返回错误 EOWNWERDEAD。

如果互斥锁具有PTHREAD_MUTEX_ROBUST_NP的属性,则应用程序在获取该锁时必须检查 pthread_mutex_lock 的返回代码看获取锁时是否返回了EOWNWERDEAD错误。如果是,则

  1. 互斥锁的新的持有者应使该互斥锁所保护的状态保持一致。因为互斥锁的上一个持有者“死亡”时互斥锁所保护的状态可能出于不一致的状态。
  2. 如果互斥锁的新的持有者能够使该状态保持一致,请针对该互斥锁调用pthread_mutex_consistent_np(),并解除锁定该互斥锁。
  3. 如果互斥锁的新的持有者无法使该状态保持一致,请勿针对该互斥锁调用pthread_mutex_consistent_np(),而是解除锁定该互斥锁。所有等待的线程都将被唤醒,以后对 pthread_mutex_lock() 的所有调用都将无法获取该互斥锁。返回错误为ENOTRECOVERABLE。

如果一个线程获取了互斥锁,但是获取时得到了EOWNERDEAD的错误,然后它终止并且没有释放互斥锁 ,则下一个持有者获取该锁时将返回代码EOWNERDEAD。

应用互斥量需要注意的几点

       1、互斥量需要时间来加锁和解锁。锁住较少互斥量的程序通常运行得更快。所以,互斥量应该尽量少,够用即可,每个互斥量保护的区域应则尽量大。

2、互斥量的本质是串行执行。如果很多线程需要领繁地加锁同一个互斥量,

则线程的大部分时间就会在等待,这对性能是有害的。如果互斥量保护的数据(或代码)包含彼此无关的片段,则可以特大的互斥量分解为几个小的互斥量来提高性能。这样,任意时刻需要小互斥量的线程减少,线程等待时间就会减少。所以,互斥量应该足够多(到有意义的地步),每个互斥量保护的区域则应尽量的少。

 

 

 

条件变量与互斥锁、信号量的区别

       1.互斥锁必须总是由给它上锁的线程解锁,信号量的挂出即不必由执行过它的等待操作的同一进程执行。一个线程可以等待某个给定信号灯,而另一个线程可以挂出该信号灯。

       2.互斥锁要么锁住,要么被解开(二值状态,类型二值信号量)。

       3.由于信号量有一个与之关联的状态(它的计数值),信号量挂出操作总是被记住。然而当向一个条件变量发送信号时,如果没有线程等待在该条件变量上,那么该信号将丢失。

       4.互斥锁是为了上锁而优化的,条件变量是为了等待而优化的,信号灯即可用于上锁,也可用于等待,因而可能导致更多的开销和更高的复杂性。

最后编辑:
作者:liujg
真实-不弄虚,不做假,做自己,不违心; 踏实-不浮躁,不盲从,不急功,不近利; 实学-不投机,不取巧,勤于学,精于业。