linux线程同步

本文详细介绍了线程同步中的互斥锁、读写锁、条件变量和信号量的概念及使用方法。互斥锁用于保护共享资源,防止数据不一致;读写锁允许多个读取者同时访问,但写入者独占;条件变量用于线程间的同步和通信,支持等待与唤醒操作;信号量作为更底层的同步机制,可用于控制资源的访问数量。在实际应用中,根据场景选择合适的同步方式,如生产者消费者模型中结合锁和条件变量实现高效通信。
摘要由CSDN通过智能技术生成

互斥锁

/*
* 互斥锁有四种属性:
* PTHREAD_MUTEX_TIMED_NP,缺省值,普通锁;
* PTHREAD_MUTEX_RECURSIVE_NP,可重入锁,允许同一个线程对同一个锁获取多次,并通过多次unlock解锁,可递归;
* PTHREAD_MUTEX_ERRORCHECK_NP,如果同一个线程请求同一个锁多次,会返回EDEADLK,可规避死锁;
* PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,在多核情况下,首先自旋获取锁,若超过了自旋次数的最大值则会阻塞挂起;
*/
//互斥锁的初始化和释放
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
              const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//以宏的形式初始化互斥锁,需将mutex声明为全局变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

//对互斥锁变量执行加锁操作,若同一进程内的其他线程已经获取了锁,则会阻塞等待获取锁的线程解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
//尝试加锁,若同一进程内其他线程已经获取了锁,该函数会立即返回(错误返回,错误号EBUSY)
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//对已经加锁了的互斥量解锁,加锁线程或同一进程中的其他线程都可以解锁普通属性的互斥锁,其他属性的互斥锁必须要加锁的线程来解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);

读写锁
/*
* 读写锁相比互斥锁:
* 读锁可以被同时获取多次,但写锁只能获取一次,并且写锁的优先级要高于读锁
* 意味着多个线程可以多次获取读锁(计数,被加锁多少次就需要释放多少次锁),不会阻塞,但是只有有一个线程获取写锁,其他线程在获取锁时都会堵塞
*/
//读写锁的初始化和释放
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
              const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
//宏常量初始化,rwlock须为全局变量
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

//对读写锁rwlock加读锁,可以同时有多个线程对读写锁变量加读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
//对读写锁rwlock加写锁,一旦线程加了写锁,其他线程在对该变量加锁时都会堵塞,若多个线程同时竞争读写锁,则写锁优先级更高
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
//对已经加锁的变量解锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

条件变量

//条件变量的初始化和释放
int pthread_cond_init(pthread_cond_t *restrict cond,
              const pthread_condattr_t *restrict attr);
int pthread_destroy(pthread_cond_t *cond);
//以宏的形式初始化条件变量,需将cond声明为全局变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

/*
* 阻塞程序,等待唤醒通知
* wait函数分为三个步骤:
* (1)解锁互斥锁mutex(2)阻塞程序(3)唤醒程序并加锁mutex
* 注意(1)(2)是一个原子操作,不可分
*/
int pthread_cond_wait(pthread_cond_t *restrict cond,
              pthread_mutex_t *restrict mutex);
//超时阻塞,若没有被其他线程唤醒,等待时间abstime后返回
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
              pthread_mutex_t *restrict mutex,
              const struct timespec *restrict abstime);

/* 若没有线程被条件变量cond阻塞,对下面两个函数没有任何影响,正常返回 */
//唤醒阻塞在条件变量上的线程,如果有多个线程被阻塞,至少唤醒一个线程(与优先级和阻塞时间相关,无惊群现象)
int pthread_cond_signal(pthread_cond_t *cond);
//同时唤醒被该条件变量阻塞的所有线程
int pthread_cond_broadcast(pthread_cond_t *cond);

生产者消费者理解

锁是锁任务队列;
(1)若单生产者,用一个条件变量用来阻塞消费者;
(2)若多生产者,用一个条件变量来阻塞消费者(队列空),一个条件变量来阻塞生产者(队列满,非必须);
(1)(2)中都可以可以用其他条件来判断是否可以生产,满了则不生产;
对于消费者来说,例如线程池,当没有任务可以消费时,必须让其阻塞;生产者则不同,阻塞不是必须的,依架构而定。

信号量

/* 无名信号量 */
/*
* 信号量的初始化和销毁
* pshared可以用来设置信号量是用于线程同步(0)还是进程同步(>0)
* value用来设置可以同时访问临界资源的访问者的数量
*/
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t *sem);

//可以理解为若信号量的值等于0,则该函数会阻塞,直到被post唤醒,若信号量的值大于0,则将信号量减1并返回
int sem_wait(sem_t *sem); 
//sem_wait的非阻塞版本,如果sem的值为0,它会立即返回,并设置错误码为EAGAIN
int sem_trywait(sem_t *sem);
//唤醒正在等待sem信号量的任意一线程,可以理解为将信号量加1
int sem_post(sem_t *sem);
//获取信号量sem当前的值,并将其保存在sval中,若有线程/进程阻塞在sem信号上,返回被阻塞线程/进程的个数
int sem_getvalue(sem_t *sem, int *sval);

/*
* 条件变量可以一次性唤醒所有等待线程,信号量没有这个功能,条件变量存在唤醒丢失,信号量不存在;
* 从实现上来说一个信号量可以是用mutex + counter(计数器) + condition variable实现的;
* 信号量的主要目的是提供一种"进程"间同步的方式;
* 条件变量需要和锁配合使用;
* 互斥量(锁)是一种特殊的信号量;
* 信号量可以看作是锁和条件变量的复合体,有锁的功能(sem为01变换时),也可以执行等待操作;
* 根据场景选用合适的同步方式,信号量最有用的场景是指明资源的数量;
* 个人理解★:信号量和条件变量主要是用于阻塞等待,如果需要访问同一临界资源,锁是必须的(保证同步).
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值