【ARM&Linux】常用线程同步的三种方法

时间:2022-12-30 15:46:53

【线程同步高效率编程】

                Linux系统中线程最大的特点就是共享性,线程同步问题较为困难也很重要,最常用的三种是:条件变量、互斥锁、无名信号量。(ps: 有名信号量可用于进程同步,无名信号量只能用于线程同步,是轻量级的。)


(一)、【互斥锁】:mutex

线程互斥量数据类型:pthread_mutex_t

  1. 初始化锁
    静态分配: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    动态分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
                    参数一:创建的互斥锁
                    参数二:存储互斥锁信息的结构,一般为NULL

  2. 加锁:对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
    int pthread_mutex_lock(pthread_mutex *mutex);
    int pthread_mutex_trylock(pthread_mutex_t *mutex);

                    参数:指明互斥锁

  3. 解锁:在完成了对共享资源的访问后,要对互斥量进行解锁
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
                    参数:指明互斥锁

  4. 销毁锁
    int pthread_mutex_destroy(pthread_mutex *mutex);
                    参数:指明互斥锁


(二)、【条件变量】:cond

                条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。
数据类型:pthread_cond_t

  1. 初始化
    静态:pthread_cond_t cond = PTHREAD_COND_INITIALIER;
    动态:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
                    参数一:指明条件变量
                    参数二:存储条件变量属性的结构>
                   
  2. 等待条件成立:释放锁,同时等待条件为真才能有停止阻塞。
    int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
                    参数一:指明条件变量
                    参数二:指明互斥锁
    int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
                   
  3. 激活条件变量:
    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞
     
  4. 注销条件变量
    int pthread_cond_destroy(pthread_cond_t *cond);

(三)、【无名信号量】:sem,

注意:链接需要加上-pthread选项
例如:gcc -pthread main.c -o main
 
                有名信号量可用于进程的同步,头文件:#include<sys/sem.h>,;而无名信号量只能用于线程,是轻量级,头文件:#include <semaphore.h>)

  1. 初始化:
    int sem_init (sem_t *sem , int pshared, unsigned int value);
                    参数一:指明信号量
                    参数二:共享选项(linux 只支持为0,即表示它是当前进程的局部信号量)
                    参数三:设置初始值
                   
  2. 等待信号量:给信号量减1,然后等待直到信号量的值大于0。
    int sem_wait(sem_t *sem);
     
  3. 释放信号量:
    int sem_post(sem_t *sem);
     
  4. 销毁信号量:
    int sem_destroy(sem_t *sem);

【DEMO】

                现有两个同学打扫卫生,学生A负责扫地,学生B负责拖地,很明显要扫完地之后在拖地,由此引入线程同步。

1、条件变量和互斥锁的联合利用实现
#include <stdio.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define DEBUG_INFO(...) printf("Info: "); printf(__VA_ARGS__)

void *student_1();
void *student_2();


int num = 0;    //共享资源
pthread_mutex_t mulock = PTHREAD_MUTEX_INITIALIZER;     //互斥锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;         //条件变量
pthread_t stu_thread[2];        //两个学生线程
int main()
{
    int i;

    // 创建两个学生线程
    pthread_create(&stu_thread[0], NULL, student_1, NULL);
    pthread_create(&stu_thread[1], NULL, student_2, NULL);

    // 等待两个线程结束
    for(i=0; i<2; i++)
    {
        pthread_join(stu_thread[i], NULL);
    }

    // 注销操作
    pthread_mutex_destroy(&mulock);
    pthread_cond_destroy(&cond);
    return 0;
}

void *student_1()
{
    int i;
    DEBUG_INFO("student a start work .\n");

    for(i=0; i<5; i++)
    {
        DEBUG_INFO("i = %d \n", i);
        pthread_mutex_lock(&mulock);    //锁住
        num++;      //扫一次地
        if(num >= 5)
        {
            DEBUG_INFO("student a finished work .\n");
            pthread_cond_signal(&cond); //通知学生B已经扫完地了,并解锁
        }

        pthread_mutex_unlock(&mulock);
        sleep(1);
    }

    pthread_exit(NULL);
    return 0;
}
void *student_2()
{
    DEBUG_INFO("in student 2 .. \n");
    while(num < 5)      //不用if四因为需要防止莫名错误
    {
        pthread_cond_wait(&cond, &mulock);  //等待学生A扫地结束,等不到会再次一直阻塞
    }

    num = 0;    //拖地
    pthread_mutex_unlock(&mulock);
    DEBUG_INFO("student b finished work .\n");
    pthread_exit(NULL);
    return 0;
}

Makefile
【ARM&Linux】常用线程同步的三种方法
运行结果
【ARM&Linux】常用线程同步的三种方法
                由运行结果可见,学生A先完成工作,学生B在完成工作,所以成功实现线程同步。

2、信号量实现
/****************************************************************************************
* 文件名: demo2.c
* 创建者: 
* 时 间: 
* 联 系: 
* 简 介: 
*****************************************************************************************/

#include <stdio.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define DEBUG_INFO(...) printf("Info: "); printf(__VA_ARGS__)

int num = 0;    //共享资源
sem_t mysem; //用于同步的信号量
pthread_t stu_thread[2];        //两个学生线程

void *student_1();
void *student_2();


int main()
{
 // 初始化信号量
 sem_init(&mysem, 0, 0);

 int i;

 // 创建两个学生线程
 pthread_create(&stu_thread[0], NULL, student_1, NULL);
 pthread_create(&stu_thread[1], NULL, student_2, NULL);

 // 等待两个线程结束
 for(i=0; i<2; i++)
 {
 pthread_join(stu_thread[i], NULL);
 }

 // 注销操作
 sem_destroy(&mysem);
 return 0;
}


void *student_1()
{
 int i;
 DEBUG_INFO("student a start work .\n");

 for(i=0; i<5; i++)
 {
 DEBUG_INFO("i = %d \n", i);
 num++; //扫一次地
 if(num >= 5)
 {
 DEBUG_INFO("student a finished work .\n");
 sem_post(&mysem); //释放信号量
 }

 sleep(1);
 }

 pthread_exit(NULL);
 return 0;
}
void *student_2()
{
 DEBUG_INFO("in student 2 .. \n");
 sem_wait(&mysem); //等待信号量 
 num = 0; //拖地
 DEBUG_INFO("student b finished work .\n");
 pthread_exit(NULL);
 return 0;
}

【运行结果】
【ARM&Linux】常用线程同步的三种方法
                由运行结果可见,用信号量的程序运行结果与使用条件变量结果一致,所以实验成功!


end..