linux下多线程同步机制之信号量、互斥量、读写锁、条件变量

时间:2022-08-06 15:13:03

之前有写过类似的博客,这东西不用老忘,现在又有更清晰的理解了。

一、信号量

编译时候加入-lrt

信号量最基本的两个操作就是PV操作:P()操作实现信号量减少,V()操作实现信号量的增加

信号量的值取决于信号量的类型,信号量的类型有多种:

(1)二进制信号量:0与1.

    当值=1时,信号量可用,当值=0时候,信号量不可用,这里就可以利用P()V()操作来实现,如果=0时候,线程阻塞在P()操作

(2)计数信号量

    某个非负整数,初始值表示可用的资源数,例如我们实现两个线程,一个线程专门执行V()操作  另一个专门执行P()操作 那么生产者,消费着的行为就是如此!!

 

//============================================================================
// Name : ThreadTest.cpp
// Author : YLF
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <semaphore.h>
#include <fcntl.h>
#include <pthread.h>

using namespace std;

char *semName = "/SEM";
sem_t *sem;


void* Producer(void* para){
int n = 10;
while(n>0){
sem_post(sem);
cout<<"produce"<<endl;
n--;
}
}

void* Consumer(void* para){
int n = 10;
while(n>0){
sem_wait(sem);
cout<<"consumer"<<endl;
n--;
}
}

int main() {

sem = sem_open(semName, O_CREAT, O_RDWR, 0);

pthread_t thread1,thread2;
pthread_create(&thread1,NULL,Producer,NULL);
pthread_create(&thread2,NULL,Consumer,NULL);

pthread_join(thread1,NULL);
pthread_join(thread2,NULL);

sem_close(sem);
sem_unlink(semName);
return 0;
}


produce
produceconsumer
consumer

produce
produce
produce
produce
produce
produce
produce
produce
consumer
consumer
consumer
consumer
consumer
consumer
consumer
consumer


我们发现,上面这种方式不是一个很好的原子操作!

当然,也可以利用0-1这种实现原子操作

就是将信号量初始化为1,然后Producer()和Consuer()这两个线程都执行

sem_wait()

受限制的资源操作

sem_post()


这种方式就可以阻塞另一个想要占用同一个资源的线程

信号量的好处就是可以实现进程间的通信!


二、POSIX还提供了另一种实现机制,利用MUTEX  pthread_mutex_t  

编译时候加入-lpthread  

#include <pthread.h>

#include <fcntl.h>


这个互斥量的使用方式

初始化:

mutex = PTHREAD_MUTEX_INITIALIZER;//静态初始化

pthread_mutex_init(&mutex, NULL);//动态初始化

请求占有:

pthread_mutex_lock(&mutex)

释放占有:

pthread_mutex_unlock(&mutex);

尝试占有:

pthread_mutex_trylock(&mutex);

销毁:

pthread_mutex_destroy(&mutex);


三、读写锁

互斥量信号量都是临界区实现,只有使用共享数据的线程或进程才能进入。可是,当我们有多个线程只是希望读取数据的时候,他们之间会进行阻塞,而我们并不希望这样,只希望写线程才会对别的产生阻塞

如果多个线程对共享内存进行写访问,则不允许其他线程进入临界区,如果应用程序有多个线程希望读,则可以进入临界区。

POSIX定义了类型为pthread_rwlock_t 的读写锁

初始化:

pthread_rwlock_init(pthread_rwlock_t*, attr)

请求占有:

pthread_rwlock_rdlock(*lock)

pthread_rwlock_wrlock(*lock);

pthread_rwlock_timedrdlock()...

释放占有:

pthread_rwlock_unlock(*lock)

尝试和销毁


四、条件变量

互斥量通过 控制 对共享数据的访问来同步任务。条件变量可以根据数据的值来同步任务。条件变量是当一个事件发生时发送信号的信号量。一旦事情发生,可能会有多个进程或线程在等待信号。条件变量通常用于对操作的顺序进行同步。

条件变量类型pthread_cond_t

初始化:

pthread_cond_init(pthread_cond_t*,*attr)

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

发送信号:

pthread_cond_signal(*cond)

pthread_cond_broadcast(*cond)

销毁

pthread_cond_destroy(*cond);

阻塞:

pthread_cond_wait(*cond,*mutex)

pthread_mutex_t mutex, eventMutex;
pthread_cond_t cond;

void* ThreadA(void *para){
int num = 10;
while(num>0){
pthread_mutex_lock(&mutex);
num--;
cout<<"ThreadA:"<<num<<endl;
if(num == 6)
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
}

void* ThreadB(void* para){
int num = 10;

pthread_mutex_lock(&eventMutex);
pthread_cond_wait(&cond, &eventMutex);
pthread_mutex_unlock(&eventMutex);
while(num>0){
pthread_mutex_lock(&mutex);
num--;
cout<<"ThreadB:"<<num<<endl;
pthread_mutex_unlock(&mutex);
}
}

int main() {

pthread_mutex_init(&mutex, NULL);
pthread_mutex_init(&eventMutex, NULL);

pthread_t thread1,thread2;
pthread_create(&thread2,NULL,ThreadB,NULL);
pthread_create(&thread1,NULL,ThreadA,NULL);

pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
return 0 ;
}