线程同步与互斥:互斥锁

来源:互联网 发布:淘宝买家v3会员是几钻 编辑:程序博客网 时间:2024/06/06 08:58

为什么需要互斥锁?

在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源。这个过程有点类似于,公司部门里,我在使用着打印机打印东西的同时(还没有打印完),别人刚好也在此刻使用打印机打印东西,如果不做任何处理的话,打印出来的东西肯定是错乱的。


下面我们用程序模拟一下这个过程,线程一需要打印“ hello ”,线程二需要打印“ world ”,不加任何处理的话,打印出来的内容会错乱:

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <pthread.h>  
  3. #include <unistd.h>  
  4.   
  5. // 打印机  
  6. void printer(char *str)  
  7. {  
  8.     while(*str!='\0')  
  9.     {  
  10.         putchar(*str);    
  11.         fflush(stdout);  
  12.         str++;  
  13.         sleep(1);  
  14.     }  
  15.     printf("\n");   
  16. }  
  17.   
  18. // 线程一  
  19. void *thread_fun_1(void *arg)  
  20. {  
  21.     char *str = "hello";  
  22.     printer(str); //打印  
  23. }  
  24.   
  25. // 线程二  
  26. void *thread_fun_2(void *arg)  
  27. {  
  28.     char *str = "world";  
  29.     printer(str); //打印  
  30. }  
  31.   
  32. int main(void)  
  33. {  
  34.     pthread_t tid1, tid2;  
  35.       
  36.     // 创建 2 个线程  
  37.     pthread_create(&tid1, NULL, thread_fun_1, NULL);  
  38.     pthread_create(&tid2, NULL, thread_fun_2, NULL);  
  39.   
  40.     // 等待线程结束,回收其资源  
  41.     pthread_join(tid1, NULL);  
  42.     pthread_join(tid2, NULL);   
  43.       
  44.     return 0;  
  45. }  

运行结果如下:



实际上,打印机是有做处理的,我在打印着的时候别人是不允许打印的,只有等我打印结束后别人才允许打印。这个过程有点类似于,把打印机放在一个房间里,给这个房间安把锁,这个锁默认是打开的。当 A 需要打印时,他先过来检查这把锁有没有锁着,没有的话就进去,同时上锁在房间里打印。而在这时,刚好 B 也需要打印,B 同样先检查锁,发现锁是锁住的,他就在门外等着。而当 A 打印结束后,他会开锁出来,这时候 B 才进去上锁打印。


而在线程里也有这么一把锁——互斥锁(mutex),互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。


互斥锁的操作流程如下:

1)在访问共享资源后临界区域前,对互斥锁进行加锁。

2)在访问完成后释放互斥锁导上的锁。

3)对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。


互斥锁的数据类型是: pthread_mutex_t


互斥锁基本操作


以下函数需要的头文件:

#include <pthread.h>


1)初始化互斥锁

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

功能:

初始化一个互斥锁。

参数:

mutex:互斥锁地址。类型是 pthread_mutex_t 。
attr:设置互斥量的属性,通常可采用默认属性,即可将 attr 设为 NULL。


可以使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化互斥锁,比如:
pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;
这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_mutex_init() 来完成动态初始化,不同之处在于 PTHREAD_MUTEX_INITIALIZER 宏不进行错误检查。

返回值:

成功:0,成功申请的锁默认是打开的。

失败:非 0 错误码


2)上锁

int pthread_mutex_lock(pthread_mutex_t *mutex);

功能:

对互斥锁上锁,若互斥锁已经上锁,则调用者一直阻塞,直到互斥锁解锁后再上锁。

参数:

mutex:互斥锁地址。

返回值:

成功:0

失败:非 0 错误码


int pthread_mutex_trylock(pthread_mutex_t *mutex);

调用该函数时,若互斥锁未加锁,则上锁,返回 0;若互斥锁已加锁,则函数直接返回失败,即 EBUSY。


3)解锁

int pthread_mutex_unlock(pthread_mutex_t * mutex);

功能:

对指定的互斥锁解锁。

参数:

mutex:互斥锁地址。

返回值:

成功:0

失败:非 0 错误码


4)销毁互斥锁

int pthread_mutex_destroy(pthread_mutex_t *mutex);

功能:

销毁指定的一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。

参数:

mutex:互斥锁地址。

返回值:

成功:0

失败:非 0 错误码


互斥锁应用实例

我们通过互斥锁完善上面的例子,示例代码如下:

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <pthread.h>  
  3. #include <unistd.h>  
  4.   
  5. pthread_mutex_t mutex; //互斥锁  
  6.   
  7. // 打印机  
  8. void printer(char *str)  
  9. {  
  10.     pthread_mutex_lock(&mutex); //上锁  
  11.     while(*str!='\0')  
  12.     {  
  13.         putchar(*str);    
  14.         fflush(stdout);  
  15.         str++;  
  16.         sleep(1);  
  17.     }  
  18.     printf("\n");   
  19.     pthread_mutex_unlock(&mutex); //解锁  
  20. }  
  21.   
  22. // 线程一  
  23. void *thread_fun_1(void *arg)  
  24. {  
  25.     char *str = "hello";  
  26.     printer(str); //打印  
  27. }  
  28.   
  29. // 线程二  
  30. void *thread_fun_2(void *arg)  
  31. {  
  32.     char *str = "world";  
  33.     printer(str); //打印  
  34. }  
  35.   
  36. int main(void)  
  37. {  
  38.     pthread_t tid1, tid2;  
  39.       
  40.     pthread_mutex_init(&mutex, NULL); //初始化互斥锁  
  41.       
  42.     // 创建 2 个线程  
  43.     pthread_create(&tid1, NULL, thread_fun_1, NULL);  
  44.     pthread_create(&tid2, NULL, thread_fun_2, NULL);  
  45.   
  46.     // 等待线程结束,回收其资源  
  47.     pthread_join(tid1, NULL);  
  48.     pthread_join(tid2, NULL);   
  49.       
  50.     pthread_mutex_destroy(&mutex); //销毁互斥锁  
  51.       
  52.     return 0;  
  53. }  


运行结果如下:


本教程示例代码下载请点此处。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 宝宝半夜咳嗽吐怎么办 小孩晚上咳嗽吐怎么办 小孩发烧手脚热怎么办 孩子发烧了呕吐怎么办 宝宝高烧后呕吐怎么办 孩子发烧39呕吐怎么办 发烧反胃想吐怎么办 孩子发烧一直吐怎么办 宝宝发烧一直吐怎么办 小孩发烧一直吐怎么办 小孩子发烧一直吐怎么办 孩子吐还拉稀怎么办 宝贝拉肚子又吐怎么办 小孩子发烧呕吐拉肚子怎么办 小孩受凉呕吐拉肚子怎么办 一周岁宝宝发烧怎么办 宝宝吃了吐怎么办 儿子喝水都吐怎么办 宝宝吐奶拉肚子怎么办 怀孕吐的厉害怎么办 孩子一直呕吐怎么办啊 仓鼠宝宝拉稀了怎么办 2岁有点拉稀怎么办 婴儿吃米粉腹泻怎么办 小儿胃寒呕吐怎么办 孩子突然一直吐怎么办 宝宝呕吐并发烧怎么办 宝宝发烧呕吐拉稀怎么办 小孩发高烧还吐怎么办 二个多月的宝宝拉肚子怎么办 宝宝肚子着凉吐怎么办 孩子胃着凉呕吐怎么办 一岁受凉呕吐怎么办 孩子着凉了呕吐怎么办 小孩受凉呕吐腹泻怎么办 宝宝胃受寒呕吐怎么办 一岁半宝宝受寒呕吐怎么办 治小儿反复发烧怎么办 海洋宝宝吃下去怎么办 两岁儿童拉肚子怎么办 14天宝宝拉肚子怎么办