Linux系统编程之线程同步高效率编程

Linux系统编程之常用线程同步的三种方法_线程同步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);​Linux系统编程之常用线程同步的三种方法_条件变量_02 参数一:创建的互斥锁
    Linux系统编程之常用线程同步的三种方法_条件变量_02
  2. 加锁:对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
    ​int pthread_mutex_lock(pthread_mutex *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex);​Linux系统编程之常用线程同步的三种方法_条件变量_02参数:指明互斥锁
  3. 解锁:在完成了对共享资源的访问后,要对互斥量进行解锁
    ​int pthread_mutex_unlock(pthread_mutex_t *mutex);​Linux系统编程之常用线程同步的三种方法_条件变量_02参数:指明互斥锁
  4. 销毁锁
    ​int pthread_mutex_destroy(pthread_mutex *mutex);​Linux系统编程之常用线程同步的三种方法_条件变量_02参数:指明互斥锁

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

Linux系统编程之常用线程同步的三种方法_线程同步条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。
数据类型:​​​pthread_cond_t​​。

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

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

注意:链接需要加上-pthread选项
例如:gcc -pthread main.c -o main
Linux系统编程之常用线程同步的三种方法_#include_14
Linux系统编程之常用线程同步的三种方法_互斥锁_08有名信号量可用于进程的同步,头文件:​​#include<sys/sem.h>,​​;而无名信号量只能用于线程,是轻量级,头文件:​​#include <semaphore.h>)​​。

  1. 初始化:
    ​​​int sem_init (sem_t *sem , int pshared, unsigned int value);​​​Linux系统编程之常用线程同步的三种方法_互斥锁_08参数一:指明信号量
    Linux系统编程之常用线程同步的三种方法_互斥锁_08参数二:共享选项(linux 只支持为0,即表示它是当前进程的局部信号量)
    Linux系统编程之常用线程同步的三种方法_互斥锁_08参数三:设置初始值
    Linux系统编程之常用线程同步的三种方法_互斥锁_08
  2. 等待信号量:给信号量减1,然后等待直到信号量的值大于0。
    ​​​int sem_wait(sem_t *sem);​​​Linux系统编程之常用线程同步的三种方法_#include_14
  3. 释放信号量:
    ​​​int sem_post(sem_t *sem);​​​Linux系统编程之常用线程同步的三种方法_#include_14
  4. 销毁信号量:
    ​​​int sem_destroy(sem_t *sem);​

【DEMO】

Linux系统编程之常用线程同步的三种方法_线程同步现有两个同学打扫卫生,学生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

Linux系统编程之常用线程同步的三种方法_互斥锁_24


运行结果

Linux系统编程之常用线程同步的三种方法_线程同步_25


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;
}
【运行结果】

Linux系统编程之常用线程同步的三种方法_互斥锁_27


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

end…