1 互斥锁
pthread_mutex_init函数
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
这里的restrict关键字,表示指针指向的内容只能通过这个指针进行修改.
restrict关键字:用来限定指针变量。被该关键字限定的指针变量所指向的内存操作,必须由本指针完成。初始化互斥量:
- 示例
pthread_mutex_t mutex;
- pthread_mutex_init(&mutex, NULL); 动态初始化。
- pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 静态初始化
相关函数
- pthread_mutex_destory函数
- pthread_mutex_lock 函数
- pthread_mutex_trylock函数
- pthread_mutex_unlock 函数**
以上5个函数的返回值都是:成功返回0,失败返回错误号
1.1 使用锁顺序
pthread_mutex_t 类型
-
pthread_mutex_t lock; 创建锁
-
pthread_mutex_init; 初始化 1
-
pthread_mutex_lock;加锁 1-- --> 0
-
访问共享数据(stdout)
-
pthrad_mutext_unlock();解锁 0++ --> 1
-
pthead_mutex_destroy;销毁锁
-
示例代码
-
访问表标准输出的时候,加上了锁。
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
void *tfn(void *arg)
{
srand(time(NULL));
while (1) {
printf("hello ");
sleep(rand() % 3); /*模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误*/
printf("world\n");
sleep(rand() % 3);
}
return NULL;
}
int main(void)
{
pthread_t tid;
srand(time(NULL));
pthread_create(&tid, NULL, tfn, NULL);
while (1) {
printf("HELLO ");
sleep(rand() % 3);
printf("WORLD\n");
sleep(rand() % 3);
}
pthread_join(tid, NULL);
return 0;
}
- 访问表标准输出的时候,加上了锁。
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t mutex; // 定义一把互斥锁
void *tfn(void *arg)
{
srand(time(NULL));
while (1) {
pthread_mutex_lock(&mutex); // 加锁
printf("hello ");
sleep(rand() % 3); // 模拟长时间操作共享资源,导致cpu易主,产生与时间有关的错误
printf("world\n");
pthread_mutex_unlock(&mutex); // 解锁
sleep(rand() % 3);
}
return NULL;
}
int main(void)
{
pthread_t tid;
srand(time(NULL));
int ret = pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
if(ret != 0){
fprintf(stderr, "mutex init error:%s\n", strerror(ret));
exit(1);
}
pthread_create(&tid, NULL, tfn, NULL);
while (1) {
pthread_mutex_lock(&mutex); // 加锁
printf("HELLO ");
sleep(rand() % 3);
printf("WORLD\n");
pthread_mutex_unlock(&mutex); // 解锁
sleep(rand() % 3);
}
pthread_join(tid, NULL);
pthread_mutex_destroy(&mutex); // 销毁互斥锁
return 0;
}
-
注意事项:
尽量保证锁的粒度, 越小越好。(访问共享数据前,加锁。访问结束【立即】解锁。)
互斥锁,本质是结构体。 我们可以看成整数。 初值为 1。(pthread_mutex_init() 函数调用成功。)
加锁: --操作, 阻塞线程。
解锁: ++操作, 唤醒阻塞在锁上的线程。
try锁:尝试加锁,成功–。失败,返回。同时设置错误号 EBUSY
2 读写锁
pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);
pthread_rwlock_rdlock(&rwlock); try
pthread_rwlock_wrlock(&rwlock); try
pthread_rwlock_unlock(&rwlock);
pthread_rwlock_destroy(&rwlock);
以上函数都是成功返回0,失败返回错误号。
pthread_rwlock_t 类型 用于定义一个读写锁变量
pthread_rwlock_t rwlock
3 条件变量
类型:pthread_cond_t 类型:用于条件变量。
- pthread_cond_init 函数
- pthread_cond_destroy 函数
- pthread_cond_wait 函数
- pthread_cond_timewait 函数
- pthread_cond_signal 函数
- pthread_cond_ broadcast 函数
- pthread_cond_t 函数
- pthread_cond_t 函数
以上6个函数的返回值都是
成功 0;
错误:失败直接返回错误号。
- pthread_cond_init 函数
-
pthread_cond_init(&cond, NULL); 动态初始化。
-
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 静态初始化。
- pthread_cond_wait 函
作用:
1) 阻塞等待条件变量满足
2) 解锁已经加锁成功的信号量 (相当于pthread_mutex_unlock(&mutex)),12两步为一个原子操作
3) 当条件满足,函数返回时,解除阻塞并重新申请获取互斥锁。重新加锁信号量 (相当于, pthread_mutex_lock(&mutex);)
- 消费者-生产者模型
/*借助条件变量模拟 生产者-消费者 问题*/
/* 互斥锁+条件变量*/
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
/*链表作为公享数据,需被互斥量保护*/
struct msg {
struct msg *next;
int num;
};
struct msg *head;
/* 静态初始化 一个条件变量 和 一个互斥量*/
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void *consumer(void *p)
{
struct msg *mp;
for (;;) {
pthread_mutex_lock(&lock);
while (head == NULL) { //头指针为空,说明没有节点 可以为if吗
pthread_cond_wait(&has_product, &lock); // 解锁,并阻塞等待
}
mp = head;
head = mp->next; //模拟消费掉一个产品
pthread_mutex_unlock(&lock);
printf("-Consume %lu---%d\n", pthread_self(), mp->num);
free(mp);
sleep(rand() % 5);
}
}
void *producer(void *p)
{
struct msg *mp;
for (;;) {
mp = malloc(sizeof(struct msg));
mp->num = rand() % 1000 + 1; //模拟生产一个产品
printf("-Produce ---------------------%d\n", mp->num);
pthread_mutex_lock(&lock);
mp->next = head;
head = mp;
pthread_mutex_unlock(&lock);
pthread_cond_signal(&has_product); //将等待在该条件变量上的一个线程唤醒
sleep(rand() % 5);
}
}
int main(int argc, char *argv[])
{
pthread_t pid, cid;
srand(time(NULL));
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&cid, NULL, consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}
- 消费者-生产者模型
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
void err_thread(int ret, char *str)
{
if (ret != 0) {
fprintf(stderr, "%s:%s\n", str, strerror(ret));
pthread_exit(NULL);
}
}
struct msg {
int num;
struct msg *next;
};
struct msg *head;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 定义/初始化一个互斥量
pthread_cond_t has_data = PTHREAD_COND_INITIALIZER; // 定义/初始化一个条件变量
void *produser(void *arg)
{
while (1) {
struct msg *mp = malloc(sizeof(struct msg));
mp->num = rand() % 1000 + 1; // 模拟生产一个数据`
printf("--produce %d\n", mp->num);
pthread_mutex_lock(&mutex); // 加锁 互斥量
mp->next = head; // 写公共区域
head = mp;
pthread_mutex_unlock(&mutex); // 解锁 互斥量
pthread_cond_signal(&has_data); // 唤醒阻塞在条件变量 has_data上的线程.
sleep(rand() % 3);
}
return NULL;
}
void *consumer(void *arg)
{
while (1) {
struct msg *mp;
pthread_mutex_lock(&mutex); // 加锁 互斥量
if (head == NULL) {
pthread_cond_wait(&has_data, &mutex); // 阻塞等待条件变量, 解锁
} // pthread_cond_wait 返回时, 重写加锁 mutex
mp = head;
head = mp->next;
pthread_mutex_unlock(&mutex); // 解锁 互斥量
printf("---------consumer:%d\n", mp->num);
free(mp);
sleep(rand()%3);
}
return NULL;
}
int main(int argc, char *argv[])
{
int ret;
pthread_t pid, cid;
srand(time(NULL));
ret = pthread_create(&pid, NULL, produser, NULL); // 生产者
if (ret != 0)
err_thread(ret, "pthread_create produser error");
ret = pthread_create(&cid, NULL, consumer, NULL); // 消费者
if (ret != 0)
err_thread(ret, "pthread_create consuer error");
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}
- 多个消费者代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
void err_thread(int ret, char *str)
{
if (ret != 0) {
fprintf(stderr, "%s:%s\n", str, strerror(ret));
pthread_exit(NULL);
}
}
struct msg {
int num;
struct msg *next;
};
struct msg *head;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 定义/初始化一个互斥量
pthread_cond_t has_data = PTHREAD_COND_INITIALIZER; // 定义/初始化一个条件变量
void *produser(void *arg)
{
while (1) {
struct msg *mp = malloc(sizeof(struct msg));
mp->num = rand() % 1000 + 1; // 模拟生产一个数据`
printf("--produce %d\n", mp->num);
pthread_mutex_lock(&mutex); // 加锁 互斥量
mp->next = head; // 写公共区域
head = mp;
pthread_mutex_unlock(&mutex); // 解锁 互斥量
pthread_cond_signal(&has_data); // 唤醒阻塞在条件变量 has_data上的线程.
sleep(rand() % 3);
}
return NULL;
}
void *consumer(void *arg)
{
while (1) {
struct msg *mp;
pthread_mutex_lock(&mutex); // 加锁 互斥量
if (head == NULL) {
pthread_cond_wait(&has_data, &mutex); // 阻塞等待条件变量, 解锁
} // pthread_cond_wait 返回时, 重写加锁 mutex
mp = head;
head = mp->next;
pthread_mutex_unlock(&mutex); // 解锁 互斥量
printf("---------consumer:%d\n", mp->num);
free(mp);
sleep(rand()%3);
}
return NULL;
}
int main(int argc, char *argv[])
{
int ret;
pthread_t pid, cid;
srand(time(NULL));
ret = pthread_create(&pid, NULL, produser, NULL); // 生产者
if (ret != 0)
err_thread(ret, "pthread_create produser error");
ret = pthread_create(&cid, NULL, consumer, NULL); // 消费者
if (ret != 0)
err_thread(ret, "pthread_create consuer error");
pthread_join(pid, NULL);
pthread_join(cid, NULL);
return 0;
}
4 信号量
信号量:
- 应用于线程、进程间同步。
- 互斥量,虽然访问是有序的,但是程序的并行性降低了。
- 信号与信号量没有关系。只是翻译的比较接近而已。
- 相当于 初始化值为 N 的互斥量。 N值,表示可以同时访问共享数据区的线程数。
API:
- sem_t sem; 定义类型。
- int sem_init(sem_t *sem, int pshared, unsigned int value);
sem_init(&sem,0 ,N);
参数:
sem: 信号量
pshared: 0: 用于线程间同步
1: 用于进程间同步
value:N值。(指定同时访问的线程数)
返回值
成功:0
错误:-1
-
sem_destroy();
-
sem_wait(); 一次调用,做一次-- 操作, 当信号量的值为 0 时
再次 – 就会阻塞。 (类比 pthread_mutex_lock) -
sem_post(); 一次调用,做一次++ 操作. 当信号量的值为 N 时,
再次 ++ 就会阻塞。(类比 pthread_mutex_unlock)
信号量的初值决定了同时访问的线程数
- 信号量实现生产者消费者模型
/*信号量实现 生产者 消费者问题*/
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
#define NUM 5
int queue[NUM]; //全局数组实现环形队列
sem_t blank_number, product_number; //空格子信号量, 产品信号量
void *producer(void *arg)
{
int i = 0;
while (1) {
sem_wait(&blank_number); //生产者将空格子数--,为0则阻塞等待
queue[i] = rand() % 1000 + 1; //生产一个产品
printf("----Produce---%d\n", queue[i]);
sem_post(&product_number); //将产品数++
i = (i+1) % NUM; //借助下标实现环形
sleep(rand()%1);
}
}
void *consumer(void *arg)
{
int i = 0;
while (1) {
sem_wait(&product_number); //消费者将产品数--,为0则阻塞等待
printf("-Consume---%d\n", queue[i]);
queue[i] = 0; //消费一个产品
sem_post(&blank_number); //消费掉以后,将空格子数++
i = (i+1) % NUM;
sleep(rand()%3);
}
}
int main(int argc, char *argv[])
{
pthread_t pid, cid;
sem_init(&blank_number, 0, NUM); //初始化空格子信号量为5, 线程间共享 -- 0
sem_init(&product_number, 0, 0); //产品数为0
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&cid, NULL, consumer, NULL);
pthread_join(pid, NULL);
pthread_join(cid, NULL);
sem_destroy(&blank_number);
sem_destroy(&product_number);
return 0;
}