POSIX信号量互斥锁自旋锁读写锁
Posted 清水寺扫地僧
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POSIX信号量互斥锁自旋锁读写锁相关的知识,希望对你有一定的参考价值。
1. 信号量和互斥锁
信号量 sem_t(使用sem_init()
或是sem_open()
),互斥锁 pthread_mutex(创建使用pthread_mutex_init()
)。
以生产者和消费者问题的实现来对POSIX信号量和互斥锁进行掌握,大体的程序思路如下。
两个信号量(sem_full,sem_empty)和一个互斥量(sem_mutex):
- sem_full 表示缓冲区是否已满,对于生产者进行生产前需先P(sem_full),对于消费者而言进行消费后需要V(sem_full);
- sem_empty 表示缓冲区是否为空,对于生产者生产后需V(sem_empty),对于消费者进行消费前需要P(sem_empty);
- sem_mutex 对缓冲区进行加锁,防止竞争条件的发生,划分临界区,保护临界资源的安全共享访问;
程序思路如下图:
程序代码如下:
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <semaphore.h>
#define ERR_EXIT(m)
do {
perror(m);
exit(EXIT_FAILURE);
} while(0)
#define CONSUMER_COUNT 1 //消费者个数
#define PRODUCER_COUNT 5 //生产者个数
#define BUFFERSIZE 10 //缓冲区大小
int g_buffer[BUFFSIZE]; //环形缓冲区,缓冲区大小为10
unsigned short in = 0; //生产的产品下标序号
unsigned short out = 0; //消费的产品下标序号
unsigned short produce_id = 0; //生产的产品序号,即生产的第几个产品
unsigned short consume_id = 0; //消费的产品序号
sem_t g_sem_full; //缓冲区满信号量,为0表示已满
sem_t g_sem_empty; //缓冲区空信号量,为0表示全空
pthread_mutex_t g_mutex; //互斥锁,设置临界区
pthread_t g_thread[CONSUMER_COUNT + PRODUCER_COUNT]; //线程数组,存储生产者和消费者线程
void* consume(void *arg) {
int num = (int)arg;
int i;
while(1) {
printf("%d wait buffer not empty", num);
sem_wait(&g_sem_empty);
//临界区
pthread_mutex_lock(&g_mutex);
//打印缓冲区中的产品状况,即打印仓库当前的状态
for(i = 0; i < BUFFERSIZE; i++) {
printf("%02d ", i);
if(-1 == g_buffer[i]) printf("%s", "null");
else printf("%d", g_buffer[i]);
if(i == out) printf("\\t<--consume");
printf("\\n");
}
consume_id = g_buffer[out];
printf("%d begin consume %d\\n", num, consume_id);
g_buffer[out] = -1;
out = (out + 1) % BUFFERSIZE;
printf("%d end consume %d\\n", num, consume_id);
pthread_mutex_unlock(&g_mutex);
sleep(5);
sem_post(&g_sem_full);
}
return NULL;
}
void* produce(void *arg) {
int num = (int)arg;
int i;
while(1) {
printf("%d wait buffer not full", num);
sem_wait(&g_sem_full);
//临界区
pthread_mutex_lock(&g_mutex);
//打印缓冲区中的产品状况,即打印仓库当前的状态
for(i = 0; i < BUFFERSIZE; i++) {
printf("%02d ", i);
if(-1 == g_buffer[i]) printf("%s", "null");
else printf("%d", g_buffer[i]);
if(i == in) printf("\\t<--produce");
printf("\\n");
}
printf("%d begin produce %d\\n", num, produce_id);
g_buffer[in] = product_id;
in = (in + 1) % BUFFERSIZE;
printf("%d end produce %d\\n", num, produce_id++);
pthread_mutex_unlock(&g_mutex);
sleep(1);
sem_post(&g_sem_empty);
}
return NULL;
}
int main() {
//将缓冲区中元素都初始化为-1,表示没有产品
for(int i = 0; i < BUFFERSIZE; i++)
g_buffer[i] = -1;
//初始化匿名信号量,第二个参数pshared为空,表明只是一个进程中的线程进行协同或是互斥,
//第三个参数表示给信号量的赋值
sem_init(&g_sem_full, 0, BUFFERSIZE);
sem_init(&g_sem_empty, 0, 0);
pthread_mutex_init(&g_mutex, NULL); //采用互斥锁的默认属性
//创建若干个线程
int i;
for(int i = 0; i < CONSUMER_COUNT; i++) //创建若干个消费者
pthread_create(&g_thread[i], NULL, consume, (void*)i);
//对于这里的i,若是传入指针可能会有竞态问题,因为线程在进入入口函数是,i对应地址值的内容有可能已被
//主线程所修改,即实际传入所创建线程的值是已经更改过的值,即出现竞态问题,所以还是使用值传递的方式为好;
//这样采用值传递同样会造成兼容性问题,解决方法是使用一个malloc获取一个int*指针指向的堆内存
//然后再将该int*内存指针作为参数传递进去,如下所示:
/*int *p = malloc(sizeof(int));
*p = i;
pthread_create(&g_thread[i], NULL, consume, p);*/
for(int i = 0; i < PRODUCER_COUNT; i++) //创建若干个生产者
pthread_create(&g_thread[i], NULL, produce, (void*)i);
for(int i = 0; i < PRODUCER_COUNT + CONSUME_COUNT; i++) //回收所有的线程
pthread_join(g_thread[i], NULL);
sem_destory(&g_sem_full); //销毁信号量
sem_destory(&g_sem_empty);
pthread_mutex_destory(&g_mutex); //销毁互斥量
return 0;
}
2. 自旋锁
3. 读写锁
分为共享锁(读锁)和排他锁(写锁):
以上是关于POSIX信号量互斥锁自旋锁读写锁的主要内容,如果未能解决你的问题,请参考以下文章