【线程同步高效率编程】
Linux系统中线程最大的特点就是共享性,线程同步问题较为困难也很重要,最常用的三种是:条件变量、互斥锁、无名信号量。(ps: 有名信号量可用于进程同步,无名信号量只能用于线程同步,是轻量级的。)
(一)、【互斥锁】:mutex
线程互斥量数据类型:
pthread_mutex_t
初始化锁
静态分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
动态分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);
参数一:创建的互斥锁
参数二:存储互斥锁信息的结构,一般为NULL加锁:对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
int pthread_mutex_lock(pthread_mutex *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
参数:指明互斥锁解锁:在完成了对共享资源的访问后,要对互斥量进行解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数:指明互斥锁销毁锁
int pthread_mutex_destroy(pthread_mutex *mutex);
参数:指明互斥锁
(二)、【条件变量】:cond
条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。
数据类型:pthread_cond_t
。
- 初始化
静态:pthread_cond_t cond = PTHREAD_COND_INITIALIER;
动态:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
参数一:指明条件变量
参数二:存储条件变量属性的结构>
- 等待条件成立:释放锁,同时等待条件为真才能有停止阻塞。
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);
- 激活条件变量:
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞
- 注销条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
(三)、【无名信号量】:sem,
注意:链接需要加上-pthread选项
例如:gcc -pthread main.c -o main
有名信号量可用于进程的同步,头文件:#include<sys/sem.h>,
;而无名信号量只能用于线程,是轻量级,头文件:#include <semaphore.h>)
。
- 初始化:
int sem_init (sem_t *sem , int pshared, unsigned int value);
参数一:指明信号量
参数二:共享选项(linux 只支持为0,即表示它是当前进程的局部信号量)
参数三:设置初始值
- 等待信号量:给信号量减1,然后等待直到信号量的值大于0。
int sem_wait(sem_t *sem);
- 释放信号量:
int sem_post(sem_t *sem);
- 销毁信号量:
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
运行结果
由运行结果可见,学生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;
}
【运行结果】
由运行结果可见,用信号量的程序运行结果与使用条件变量结果一致,所以实验成功!
end..