自学笔记-同步简述

  • 互斥锁 MUTEXS
  • 信号量 SEMAPHORES
  • 读写锁 READ-WRITE LOCKS

互斥锁 MUTEXS

保护临界区

以确保只有一个线程或者进程可以同时执行临界区内的代码

目的:互斥的访问共享资源,避免并发访问出现的竞争

核心思想:进入临界区之前先获取锁,退出临界区之后释放锁

临界区保护代码示例:

//定义互斥锁
pthread_mutex_t mutex; //互斥锁类型
pthread_mutex_init(&mutex, NULL); //创建并初始化

//加锁 
pthread_mutex_lock(&mutex);

//临界区代码
//对共享资源进行操作

//解锁
pthread_mutex_unlock(&mutex);

//销毁互斥锁 
pthread_mutex_destroy(&mutex);

任意时刻只有一个线程或者进程可以进入临界区,保证了共享资源的安全访问

下面是一个更为直接的例子

lock_the_mutex(…); //获取互斥锁
critical region;  //在临界区执行操作
unlock_the_mutex(…); //释放互斥锁

互斥锁的声明

#include <pthread.h>

static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

如果互斥锁变量是静态分配的,我们可以将其初始化为常量  PTHREAD_MUTEX_INITIALIZER

这样就可以直接使用lock作为互斥锁,并且他被自动初始化为一个可用的互斥锁

#include <pthread.h>

static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

int main() {
    // 在代码中使用互斥锁
    pthread_mutex_lock(&lock);
    // 临界区
    pthread_mutex_unlock(&lock);

    return 0;
}

使用静态初始化的互斥锁可以简化代码(更方便)

但是该互斥锁无法动态销毁(避免在不再需要的时候重复使用静态初始化的互斥锁)

互斥锁的操作函数示例

#include <pthread.h>

//以下的mptr都是指向互斥锁变量的指针
//操作成功返回0,错误返回错误码

//初始化互斥锁
//attr(通常设置为null)指向互斥锁属性
int pthread_mutex_init(pthread_mutex_t *mptr, const pthread_mutexattr_t *attr);

//加锁
//如果当前的互斥锁被其他线程锁定,则该函数会阻塞并且等待,直到互斥锁可用
int pthread_mutex_lock(pthread_mutex_t *mptr);

//解锁
//只有有锁的线程才能解锁
int pthread_mutex_unlock(pthread_mutex_t *mptr);

//销毁互斥锁
//销毁之前确保已经被解锁
int pthread_mutex_destroy(pthread_mutex_t *mptr);

pthread_mutex_trylock函数

可以获取互斥锁,并根据返回值判断是否获取成功

返回ebusy(错误码) ,则说明互斥锁已经被其他线程占用,可以等待一段时间后重试或者进行其他操作

生产者消费者问题:

涉及一个或多个生产者生成数据项,然后由一个或多个消费者处理

这中情况下我们可以

使用互斥锁

使用条件变量

#include <pthread.h>
#define MAX_BUFFER_SIZE 10

//共享数据定义为buff
int buff[MAX_BUFFER_SIZE];
int count = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void *producer(void *arg) {
    while (true) {
        // 生成数据项

        pthread_mutex_lock(&mutex);
        // 将项添加到 buff
        count++;
        if (count == 1) {
            // 如果缓冲区为空,则通知消费者
            pthread_cond_signal(&cond); 
        }
        pthread_mutex_unlock(&mutex);
    }
}

void *consumer(void *arg) {
    while (true) {
        pthread_mutex_lock(&mutex);
        while (count == 0) {
            // 等待项目可用
            pthread_cond_wait(&cond, &mutex); 
        }
        // 从 buff 中消费项
        count--;
        pthread_mutex_unlock(&mutex);
    }
}

int main() {
    // 创建生产者和消费者线程

    // 加入线程

    return 0;
}

信号量 SEMAPHORES

信号量是一种原语,用于在各个进程之间给定进程中的各个线程之间提供同步

//操作:
Create a semaphore: //创建一个信号量,指定初始值。
Wait for a semaphore: //等待一个信号量,如果值为0则阻塞等待。
Post to a semaphore: //发送信号到一个信号量,增加信号量的值。

一个二进制信号量可以像互斥锁一样用于互斥操作

#include <semaphore.h>

//初始化二进制信号量
//sem 指向信号量的指针
//shared 指定信号量是否在不同进程间共享,如果为0,则信号量在进程的线程之间共享,否则在进程之间共享
//value 设置信号量的初始值
int sem_init(sem_t * sem, int shared, unsigned int value);

/*
sem_wait;
sem_post;
*/

//销毁信号量,释放相关资源
int sem_destroy(sem_t * sem);
  • 如果一个内存信号量在单个进程的线程之间共享,那么该信号量具有进程持久性,并在进程终止时消失
  • 如果一个内存信号量在不同进程之间共享,那么该信号量必须存储在共享内存中,并且只要共享内存存在,信号量就会一直存在

几个操作信号量的函数

sem_wait

用于测试指定信号量的值,

大于0 则减一,函数立即返回

等于0,调用线程则会被设置为睡眠状态,直到他大于0,然后减一,并返回函数

int sem_wait(sem_t *sem);

sem_trywait

如果信号量为0,不会进入睡眠状态

int sem_trywait(sem_t *sem);

sem_post

一个线程完成对信号量的使用时调用

将信号量的值加1,并且唤醒正在等待信号值变为整数的任何线程

int sem_post(sem_t *sem);

sem_getvalue

valp 所指向整数的当前信号量值

如果当前信号量被锁定,则返回值要么是0,要么是一个负数

其绝对值是正在等待信号量解锁的线程数

int sem_getvalue(sem_t *sem, int *valp);

读写锁 READ-WRITE LOCKS

同步原语

允许多个线程同时读取共享资源,但是只允许一个线程写入

规则:

1 没有线程持有读写锁写入时,可以有任意数量的线程同时持有读写锁进行读取,允许多个线程并发的读取,并且不会被阻塞

2 当没有其他线程读取或者写入时,线程才能进行写入,避免对共享数据并发的修改

这种规则适用于,读取频率远远高于写入频率 的情况,读写锁比互斥锁效率更高

#include <pthread.h>

//读写锁的数据类型是pthread_rwlock_t
//分配了此类型的静态变量,可以通过将constat PTHREAD_RWLOCK_INITIALIZER赋值进行初始化
int pthread_rwlock_init(pthread_rwlock_t *rwptr, const pthread_rwlockattr_t *attr);
//获取一个读锁,读写锁当前由写者持有,则阻塞调用线程
int pthread_rwlock_rdlock(pthread_rwlock_t *rwptr);
//获取一个写锁,如果现在被其他写者持有,或者一个或多个线程持有,则阻塞进程
int pthread_rwlock_wrlock(pthread_rwlock_t *rwptr);
//释放一个读锁或者写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwptr);


//成功返回0,错误返回正数值

tryrdlock

trywrlock

尝试获取读锁或者写锁

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwptr);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwptr);

如果锁无法被获取,则返回ebusy错误(不会将调用线程设置为睡眠状态)

成功返回0,失败返回positive Exxx value

当线程不再需要读写锁时,可以调用函数来销毁它

int pthread_rwlock_destroy(pthread_rwlock_t *rwptr);

成功返回0,失败返回positive Exxx value

本篇文章为自学笔记,全部内容为笔者个人理解,如有缺漏或错误,请帮忙评论留言指正,感谢大家!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值