• Linux C 线程间同步机制


    概述

      同进程内的所有线程共同使用进程的内存空间,并且线程可以在直接应用层完成,因此线程和线程之间的通信使用“全局变量”即可完成通信。但是由于 Linux 系统没有数据保护方式,所在在线程通信的时候需要进行数据的“同步保护”。
      在同进程中的多个线程都要操作的数据进行保护时,只允许一个线程操作要保护的内容。Linux 系统使用“信号量”、“互斥锁”以及“条件变量”来实现数据同步保护。实现数据保护的流程为:
    ①创建一种使用的保护机制(信号量、互斥锁或条件变量)。
    ②在操作保护内容之前,加保护(一旦本线程加保护成功,其他线程在操作就会阻塞)。
    ③在操作保护内容后,解保护(其他线程自动取消阻塞)。
    ④删除保护机制。
    注:线程间的数据保护机制是一种人为的约束,一定要所有线程都要遵循这个操作才会有意义,同时“加保护”、“解保护”操作在一个线程中一定要成对出现。

    保护机制

    互斥锁

    创建互斥锁  pthread_mutex_init

    头文件
      #include
    函数原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
    参数介绍:
      mutex:互斥锁编号。
      attr:互斥锁的属性。
    在这里插入图片描述
    返回值:成功返回 0,失败返回-1,并且设置 errno 变量来指示错误的发生。

    	pthread_mutex_t mutexid;
    	pthread_mutex_init(&mutexid,NULL);
    
    • 1
    • 2

    加锁  pthread_mutex_lock

    头文件
      #include
    函数原型:int pthread_mutex_lock(pthread_mutex_t *mutex);
    参数介绍:
      mutex:互斥锁编号。
    返回值:成功返回 0,失败返回-1,并且设置 errno 变量来指示错误的发生。

    	pthread_mutex_lock(&mutexid);
    
    • 1

    解锁  pthread_mutex_unlock

    头文件
      #include
    函数原型:int pthread_mutex_unlock(pthread_mutex_t *mutex);
    参数介绍:
      mutex:互斥锁编号。
    返回值:成功返回 0,失败返回-1,并且设置 errno 变量来指示错误的发生。
    返回值:

    	pthread_mutex_unlock(&mutexid);
    
    • 1

    删除锁  pthread_mutex_destroy

    头文件
      #include
    函数原型:int pthread_mutex_destroy(pthread_mutex_t *mutex)
    参数介绍:
      mutex:互斥锁编号。
    返回值:成功返回 0,失败返回-1,并且设置 errno 变量来指示错误的发生。
    返回值:

    	pthread_mutex_destroy(&mutexid);
    
    • 1

    条件变量

    创建条件变量  pthread_cond_init

    头文件
      #include
    函数原型:int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
    参数介绍:
      cond:条件变量编号。
      mutex:条件变量的属性。
    返回值:成功返回 0,失败返回-1,并且设置 errno 变量来指示错误的发生。

    	pthread_cond_t condid;
    	pthread_cond_init(&condid,NULL);
    
    • 1
    • 2

    激活条件变量  pthread_cond_signal

    头文件
      #include
    函数原型:int pthread_cond_signal(pthread_cond_t *cond);
    参数介绍:
      cond:条件变量编号。
    返回值:成功返回 0,失败返回-1,并且设置 errno 变量来指示错误的发生。

    	pthread_cond_signal(&condid);
    
    • 1

    等待条件变量  pthread_cond_wait

    头文件
      #include
    函数原型:int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
    参数介绍:
      cond:条件变量编号。
      mutex:条件变量的属性。
    返回值:成功返回 0,失败返回-1,并且设置 errno 变量来指示错误的发生。

    	pthread_cond_wait(&condid,&mutexid);
    
    • 1

    删除条件变量  pthread_cond_destroy

    头文件
      #include
    函数原型:int pthread_cond_destroy(pthread_cond_t *cond);
    参数介绍:
      cond:条件变量编号。
    返回值:成功返回 0,失败返回-1,并且设置 errno 变量来指示错误的发生。

    	pthread_cond_destroy(&condid);
    
    • 1

    信号灯

    创建信号量  sem_init

    头文件
      #include
    函数原型:==int sem_init(sem_t *sem, int pshared, unsigned int value); ==
    参数介绍:
      sem:信号量编号。
      pshared:信号量在作用范围(0:本进程中多个线程共享可用;非 0:在当前登录用户的多个进程之间共享)。
      value:信号量的信号值。
    返回值:成功返回 0,失败返回-1,并且设置 errno 变量来指示错误的发生。

    	sem_t semid;
    	sem_init(&semid,0, 1);
    
    • 1
    • 2

    信号量加保护  sem_wait

    头文件
      #include
    函数原型:int sem_wait(sem_t * sem);
    参数介绍:
        sem:信号量编号。
    返回值:成功返回 0,失败返回-1,并且设置 errno 变量来指示错误的发生。

    	sem_wait(&semid);
    
    • 1

    信号量解保护  sem_post

    头文件
      #include
    函数原型:int sem_post(sem_t * sem);
    参数介绍:
        sem:信号量编号。
    返回值:成功返回 0,失败返回-1,并且设置 errno 变量来指示错误的发生。

    	sem_post(&semid);
    
    • 1

    信号量删除  sem_destroy

    头文件
      #include
    函数原型:int sem_destroy(sem_t * sem);
    参数介绍:
        sem:信号量编号。
    返回值:成功返回 0,失败返回-1,并且设置 errno 变量来指示错误的发生。

    	sem_destroy(&semid);
    
    • 1

    线程间通信的例子

    在这里插入图片描述

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    char path[10];
    //信号服务函数
    void * son_fun(void * arg)
    {
    	int fd2 = open(path,O_RDWR);		//打开读管道
    	while(1)
    	{
    		char rb[512] = {0};
    		read(fd2,rb,sizeof(rb));	
    		printf("%s说:%s\n",(char *)arg,rb);	
    	}
    	close(fd2);							//关闭读管道
    }
    //关于argv:
    // 1 是读通道  2 是写通道  3 是自己的信号  4 是发的信号
    int main(int argc,char *argv[])
    {
    	//init
    	pthread_t id;
    	strcpy(path,argv[2]);
    	//创建管道
    	int val1 = mkfifo(argv[1],0666);
    	int val2 = mkfifo(argv[2],0666);
    	//打开管道
    	int fd1 = open(argv[1],O_RDWR); 		//写管道
    	//创建子线程
    	pthread_create(&id,NULL,son_fun,"he say");//创建子线程
    	//等待写入
    	while(1)
    	{
    		char wb[512] = {0};
    		gets(wb);
    		write(fd1,wb,strlen(wb));
    	}
    	close(fd1);
    	pthread_join(id,NULL);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
  • 相关阅读:
    超详细反编译python打包的exe
    软件测试流程分享
    【毕业设计】基于单片机的手势检测识别系统 - arduino 物联网嵌入式
    【C++天梯计划】1.10 二叉树(binary tree)
    用Hugging Face Transformers,高效部署:多显卡量化感知训练并转换为ONNX格式的多标签分类模型
    学科排名TOP4|教育学老师CSC获批赴美访学交流
    从C过渡到C++(1)——GNU/Linux
    C++ 左值、右值、左值引用以及右值引用
    VBA技术资料MF84:判断文件夹是否存在并创建
    ES——Fluent-bit——kibana组建日志收集系统---docker方式部署
  • 原文地址:https://blog.csdn.net/weixin_52604835/article/details/134449497