目录
Linux 线程同步和互斥:互斥锁
互斥锁概念
mutex
是一种简单的加锁的方法来控制对共享资源的访问。在同一时刻只能有一个线程掌握某个互斥上的锁
,
拥有上锁状态的线程能够对共享资源进行访问。若其他线程希望上锁一个已经被上了互斥锁的资源,则该线程挂
起,直到上锁的线程释放互斥锁为止。
互斥锁分类
静态定义互斥锁的变量
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
//快速互斥锁,
缺省的
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
//递归互斥锁,可以多次加锁
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
//检错互斥锁,快速互斥锁的非阻塞版本
静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_init (&mutex, NULL);
动态初始化
pthread_mutex_t mutex; //创建锁对象
pthread_mutexattr_t mutexattr; //创建锁的属性对象
pthread_mutexattr_init (&mutexattr); //初始化属性对象
pthread_mutexattr_settype (&mutexattr, PTHREAD_MUTEX_TIMED_NP); //快速互斥锁
// pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE_NP); //递归互斥锁
// pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK_NP); //检错互斥锁
pthread_mutex_init (&mutex, &mutexattr); //用属性对象初始化锁
pthread_mutexattr_destroy(&mutexattr); //销毁
mutex
属性对象
实现步骤
互斥锁初始化函数:pthread_mutex_init
互斥锁上锁函数:pthread_mutex_lock
互斥锁判断上锁:pthread_mutex_trylock
如果一个线程既想获得锁,又不想挂起等待,可调用该函数,如果
mutex
已经被另一个线程获得,这个函数会失败返回-EBUSY
,而不会使线程挂起等待。避免死锁。
互斥锁解锁函数:pthread_mutex_unlock
消除互斥锁函数:pthread_mutex_destroy
int pthread_mutex_destroy( pthread_mutex_t * mutex);
/*
主线程:用死循环完成对循环变量i++计数并赋值给两个全局变量vl1,vl2
子线程:判断是否vl1和vl2是否相等,不等打印两个变量的值
*/
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
int globa_v1,globa_v2;
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //创建互斥锁对象
pthread_mutex_t mutex ; //创建互斥锁对象
void * thread(void *arg)
{ //线程体:并发执行代码的入口这里开始
while (1) {
pthread_mutex_lock(&mutex);
if (globa_v1 != globa_v2)
printf("globa_v1=%d, globa_v2=%d\n", globa_v1, globa_v2);
pthread_mutex_unlock(&mutex);
}
pthread_exit(0);
}
int main(void)
{
pthread_t tid;
int i;
pthread_create(&tid, NULL, thread, NULL);
for(i=0;; i++){
pthread_mutex_lock(&mutex);
globa_v1=i+1;
//时间片切换出去了,出现数据不一致
globa_v2=i+1;
pthread_mutex_unlock(&mutex);
}
//等待线程结束,回收线程资源
pthread_join(tid, NULL);
return 0;
}
/*
进程1循环计数15次,每到3的倍数,调用条件通知函数,发信号唤醒进程2
进程2判断循环计数是否为3的倍数,是调用条件等待函数,阻塞进程2
需要用到两个对象:互斥锁对象 条件变量对象
*/
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/*初始化互斥锁*/
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;/*初始化条件变量*/
int i=1;
void * thread1(void *arg)
{ //线程体:并发执行代码的入口这里开始
pthread_detach(pthread_self()); //分离线程,不用join回收
for (i=1; i<=15; i++) {
if (i%3==0) usleep(200000); //200ms 确保要得到通知的线程进入一个阻塞等待通知状态
pthread_mutex_lock(&mutex); //上锁
if (i % 3 ==0) { //条件测试
pthread_cond_signal(&cond); //条件成立,发送信号给进程2
} else {
printf("thread1: %d\n", i);
}
pthread_mutex_unlock(&mutex); //解锁
usleep(500000);
}
pthread_exit(0);
}
void * thread2(void *arg)
{ //线程体:并发执行代码的入口这里开始
while(i<15) {
pthread_mutex_lock(&mutex); //上锁
if(i%3 == 0) {
pthread_cond_wait(&cond, &mutex); // 阻塞,直到等待信号通知我,解除阻塞
printf("thread2:%d\n", i);
}
pthread_mutex_unlock(&mutex); //解锁
}
pthread_exit(0);
}
int main(void)
{
pthread_t tid1,tid2;
//初始化互斥锁和条件变量
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
printf("main thread : start\n");
pthread_create(&tid1, NULL, thread1, NULL);
pthread_create(&tid2, NULL, thread2, NULL);
//等待线程结束,回收线程资源
pthread_join(tid2, NULL);
printf("main thread : over\n");
//销毁两个对象
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}
Linux 线程同步和互斥:条件变量
条件变量相关概念
条件变量:它是以
互斥锁
为基础;
包括两个动作:一个线程等待某个条件为真才能继续往下执行,现在这个条件不成立,而将自己挂起等待;
另一个线程在执行过程中使这个条件成立,就唤醒挂起等待的线程继续执行。
pthread
库中通过条件变量来阻塞等待一个条件,或唤醒等待这个条件的线程。
条件等待的使用场景:
多线程访问一个互斥区域内的资源,如果获得资源的条件不够时,则线程需要等待,直到其他线程释放资源后,唤醒等待条件(
等待队列的线程
)
,使得线程得以继续。
条件变量的创建和销毁
条件变量用
pthread_cond_t
类型的变量表示,创建方式如下:
pthread_cond_t cond;
条件变量的初始化,条件变量在使用前必须初始化,有两种初始化方式:
1)静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
2
)动态初始化,初始化函数如下:
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
参数:
cond:
是一个需要初始化的
pthread_cond_t
类型的指针;
attr:
是初始化
cond
为
attr
属性的条件变量,若要以默认属性初始化
cond
,则传入
NULL;
函数调用成功,返回
0
,否则返回一个非
0
的错误码。
条件变量的销毁
int pthread_cond_destroy(pthread_cond_t *cond);
函数调用成功返回
0
,否则返回非
0
的错误码。
条件变量的使用
条件变量一般与条件测试、互斥量、条件等待函数、条件通知函数一起使用;
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/*初始化互斥锁*/
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;/*初始化条件变量*/
void *thread1(void *);
void *thread(void *);
int i=1;
int main(void)
{
pthread_t t_a;
pthread_t t_b;
pthread_t t_c;
pthread_t t_d;
int b=2,c=3,d=4;
/*初始化互斥锁和条件变量*/
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
printf("main thread: work start\n");
pthread_create(&t_a,NULL,thread1, NULL);/*创建进程t_a*/
pthread_create(&t_b,NULL,thread, &b); /*创建进程t_b*/
pthread_create(&t_c,NULL,thread, &c);/*创建进程t_c*/
pthread_create(&t_d,NULL,thread, &d); /*创建进程t_d*/
pthread_join(t_a, NULL);/*等待进程t_a结束*/
printf("main: work finished!\n");
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
void *thread1(void *arg)
{
for(i=1;i<=15;i++) {
if(i%3 == 0)usleep(200000);//sleep0.2s,确保想要得到我的信号通知的线程都进入一个阻塞等待通知的状态去
pthread_mutex_lock(&mutex);/*锁住互斥量*/
if(i%3 == 0) {
//pthread_cond_signal(&cond);/*条件改变,给某个线程发送单个信号,t_b, t_c,t_d谁能收到不知道*/
pthread_cond_broadcast(&cond);
}
else
printf("thead1:%d\n",i);
pthread_mutex_unlock(&mutex);/*解锁互斥量*/
sleep(1);
}
return NULL;
}
void *thread(void *arg)
{
int no=*((int *)arg);
pthread_detach(pthread_self()); //分离线程
while(i<15) {
pthread_mutex_lock(&mutex); //加锁
if(i%3 == 0) {
pthread_cond_wait(&cond,&mutex);/*阻塞等待“信号”通知*/
printf("thread%d:%d\n", no, i);
}
pthread_mutex_unlock(&mutex); //解锁
usleep(200000);//sleep 0.2s
}
return NULL;
}