Linux系统编程-(pthread)线程通信(互斥锁)
2022/1/12 7:04:47
本文主要是介绍Linux系统编程-(pthread)线程通信(互斥锁),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
这篇文章介绍Linux下线程同步与互斥机制–互斥锁,在多线程并发的时候,都会出现多个消费者取数据的情况,这种时候数据都需要进行保护,比如: 火车票售票系统、汽车票售票系统一样,总票数是固定的,但是购票的终端非常多。
互斥锁就是用来保护某一个资源不能同时被2个或者2个以上的线程使用。
为什么需要加锁?
就是因为多个线程共用进程的资源,要访问的是公共区间时(全局变量),当一个线程访问的时候,需要加上锁以防止另外的线程对它进行访问,以实现资源的独占。在一个时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程才能够对共享资源进行操作。若其他线程希望上锁一个已经上锁了的互斥锁,则该线程就会挂起,直到上锁的线程释放掉互斥锁为止。
1. 互斥锁介绍
在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
Linux系统下定义了一套专门用于线程互斥的mutex函数。
mutex 是一种简单的加锁的方法来控制对共享资源的存取,这个互斥锁只有两种状态(上锁和解锁),可以把互斥锁看作某种意义上的全局变量。
总结: 互斥锁可以保护某个资源同时只能被一个线程所使用。
2. 互斥锁相关的函数
#include <pthread.h> //销毁互斥锁 int pthread_mutex_destroy(pthread_mutex_t *mutex); //初始化互斥锁 int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); //上锁: 阻塞方式 int pthread_mutex_lock(pthread_mutex_t *mutex); //上锁: 非阻塞方式 int pthread_mutex_trylock(pthread_mutex_t *mutex); //解锁 int pthread_mutex_unlock(pthread_mutex_t *mutex); 说明: 对于Linux下的信号量/读写锁文件进行编译,需要在编译选项中指明-D_GNU_SOURCE 否则用gcc编译就会出现 PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP未声明(在此函数内第一次使用) 这样的提示。 例如: $ gcc app.c -lpthread -D_GNU_SOURCE
2.1 初始化互斥锁
头文件 #include <pthread.h> 定义函数 int pthread_mutex_init( pthread_mutex_t *mutex, const pthread_mutex_attr_t* attr ); 函数说明 该函数初始化一个互斥体变量,如果参数attr 为NULL,则互斥体变量mutex 使用默认的属性。
2.2 销毁互斥锁
头文件 #include <pthread.h> 定义函数 int pthread_mutex_destroy ( pthread_mutex_t *mutex ); 函数说明 该函数用来释放分配给参数mutex 的资源。 返回值 调用成功时返回值为 0, 否则返回一个非0 的错误代码。
2.3 上锁
头文件 #include <pthread.h> 定义函数 int pthread_mutex_lock( pthread_mutex_t *mutex ); 函数说明 该函数用来锁住互斥体变量。如果参数mutex 所指的互斥体已经被锁住了,那么发出调用的线程将被阻塞直到其他线程对mutex 解锁。 如果上锁成功,将返回0。
2.4 尝试上锁-非阻塞
表头文件 #include <pthread.h> 定义函数 int pthread_mutex_trylock( pthread_t *mutex ); 函数说明 该函数用来锁住mutex 所指定的互斥体,但不阻塞。 返回值 如果该互斥体已经被上锁,该调用不会阻塞等待,而会返回一个错误代码。 如果上锁成功,将返回0.
2.5 解锁
头文件 #include <pthread.h> 定义函数 int pthread_mutex_unlock( pthread_mutex_t *mutex ); 函数说明 该函数用来对一个互斥体解锁。如果当前线程拥有参数mutex 所指定的互斥体,该调用将该互斥体解锁。 如果解锁成功,将返回0. 说明: 对同一个锁多次解锁没有叠加效果,如果锁是上锁状态,那么多次解锁也只有一次有效。
3. 互斥锁框架运用模型
pthread_mutex_t mutex; void 线程1(void) { while(1) { //上锁 pthread_mutex_lock(&mutex); .....主体代码...... //解锁 pthread_mutex_unlock(&mutex); } } void 线程2(void) { while(1) { //上锁 pthread_mutex_lock(&mutex); .....主体代码...... //解锁 pthread_mutex_unlock(&mutex); } } int main(void) { //初始化互斥锁 pthread_mutex_init(&mutex,NULL); .....主体代码...... //销毁互斥锁 pthread_mutex_destroy(&mutex); }
4. 案例代码: 对公共函数上锁保护
下面代码是两个线程同时调用了一个打印函数,分别打印: “123” “456”。
void print(char *p) { while(*p!='\0') { printf("%c",*p++); sleep(1); } } void *thread1_func(void *arg) { print("123\n"); } void *thread2_func(void *arg) { print("456\n"); }
如果不保护,默认的打印结果:
[wbyq@wbyq linux-share-dir]$ ./a.out 412536
预期的结果应该是打印123\456连续在一起的,对于这种情况,就可以加锁进行保护。
上锁的示例代码:
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <dirent.h> #include <stdlib.h> #include <pthread.h> pthread_mutex_t mutex; void print(char *p) { while(*p!='\0') { printf("%c",*p++); sleep(1); } } /* 线程工作函数 */ void *thread_work_func(void *dev) { while(1) { //上锁 pthread_mutex_lock(&mutex); print("123\n"); //解锁 pthread_mutex_unlock(&mutex); usleep(1000*10); } } /* 线程工作函数 */ void *thread_work_func2(void *dev) { //上锁 pthread_mutex_lock(&mutex); print("456\n"); //解锁 pthread_mutex_unlock(&mutex); usleep(1000*10); } int main(int argc,char **argv) { //初始化互斥锁 pthread_mutex_init(&mutex,NULL); /*1. 创建子线程1*/ pthread_t thread_id; if(pthread_create(&thread_id,NULL,thread_work_func,NULL)!=0) { printf("子线程1创建失败.\n"); return -1; } /*2. 创建子线程2*/ pthread_t thread_id2; if(pthread_create(&thread_id2,NULL,thread_work_func2,NULL)!=0) { printf("子线程2创建失败.\n"); return -1; } /*3. 等待线程的介绍*/ pthread_join(thread_id,NULL); pthread_join(thread_id2,NULL); //销毁互斥锁 pthread_mutex_destroy(&mutex); return 0; }
5. 案例代码: 模拟火车票售卖系统(保护同一个全局变量)
下面代码模拟一个火车票售卖系统,此处不加锁,可能会出现卖出负数票的情况。
#include <stdio.h> #include <pthread.h> int cnt = 121; //火车票,公共资源(全局) void* pthread1(void* args) { while(ticketcount > 0) { printf("窗口A开始售票,门票是:%d\n",cnt); sleep(2); cnt--; printf("窗口A售票结束,最后一张车票是:%d\n",cnt); } } void* pthread2(void* args) { while(cnt > 0) { printf("窗口B开始售票,车票是:%d\n",cnt); sleep(2); cnt--; printf("窗口B售票结束,最后一张车票是:%d\n",cnt); } } int main() { pthread_t pthid1 = 0; pthread_t pthid2 = 0; pthread_create(&pthid1,NULL,pthread1,NULL); pthread_create(&pthid2,NULL,pthread2,NULL); pthread_join(pthid1,NULL); pthread_join(pthid2,NULL); return 0; }
加锁之后的火车售票系统
#include <stdio.h> #include <pthread.h> int cnt = 121; pthread_mutex_t lock; void* pthread1(void* args) { while(1) { pthread_mutex_lock(&lock); //因为要访问全局的共享变量,所以就要加锁 if(cnt > 0) //如果有票 { printf("窗口A开始售票,车票是:%d\n",cnt); sleep(2); cnt--; printf("窗口A售票结束,最后一张车票是:%d\n",cnt); } else { pthread_mutex_unlock(&lock); pthread_exit(NULL); } pthread_mutex_unlock(&lock); sleep(1); //要放到锁的外面,让另一个有时间锁 } } void* pthread2(void* args) { while(1) { pthread_mutex_lock(&lock); if(cnt>0) { printf("窗口B开始售票--车票是:%d\n",cnt); sleep(2); cnt--; printf("窗口B售票结束,最后一张车票是:%d\n",cnt); } else { pthread_mutex_unlock(&lock); pthread_exit(NULL); } pthread_mutex_unlock(&lock); sleep(1); } } int main() { pthread_t pthid1 = 0; pthread_t pthid2 = 0; //初始化锁 pthread_mutex_init(&lock,NULL); //创建线程 pthread_create(&pthid1,NULL,pthread1,NULL); pthread_create(&pthid2,NULL,pthread2,NULL); //等待线程结束 pthread_join(pthid1,NULL); pthread_join(pthid2,NULL); //销毁锁 pthread_mutex_destroy(&lock); return 0; }
这篇关于Linux系统编程-(pthread)线程通信(互斥锁)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法