多线程编程4:linux线程同步(信号量和读写锁)

本文详细介绍了Linux中的信号量API及其用法,包括sem_init、sem_destroy等函数,以及读写锁(如pthread_rwlock)的原理、初始化和使用,特别提到了读写锁的优先级设置,展示了如何通过设置实现读写操作的并发控制。
摘要由CSDN通过智能技术生成

信号量

  • linux信号量API:

    #include <semaphore.h>
    // 定义信号量对象
     sem_t sem;
    /*初始化信号量
     	- 第一个参数:信号量地址
     	- 第二个参数:线程同步(0),进程同步(非0)
     	- 第三个参数:初始化信号量资源数(>=0),如果设置为0,线程会被阻塞
     	- 成功返回0,失败返回-1
    */
    int sem_init(sem_t* sem, int pshared, unsigned int value);
    // 销毁信号量
    int sem_destroy(sem_t* sem);
    // 给信号量资源+1,会唤醒其他wait阻塞的线程
    int sem_post(sem_t* sem);
    // 获取一个信号量里面的资源(资源-1),资源数为0阻塞
    int sem_wait(sem_t* sem);
    // 尝试获取,若资源数为0,不阻塞直接返回错误码
    int sem_trywait(sem_t* sem);
    // 超时获取资源,资源数为0,只需阻塞abs_timeout时间
    int sem_timedwait(sem_t* sem, const struct timespec* abs_timeout);
    // 查看当前信号量资源数,sval是传出参数
    int sem_getvalue(sem_t *sem,int *sval);
    
  • TIP:

    • 当资源数只有1的时候,不管多少个线程,可以工作的线程只有一个,其他线程拿不到资源会被阻塞,或者说是有多少资源,唤醒多少线程与mutex不同,mutex是大家一起强, 与条件变量不同,条件变量要么只唤醒一个要么全部唤醒。
    • 当资源数大于1的时候,需要配合mutex维持共享资源的顺序访问
    • 使用sem_timedwait的时候,时间参数不能设置为NULL,不然运行的时候会产生崩溃

读写锁

  • 背景:
    很多情况下,对于共享变量的访问,读操作远多于写操作,这种情况下读操作是不需要同步的,可以安全的并发访问,如果使用mutex导致读的性能严重下降

  • 初始化销毁读写锁

    #include <pthread.h>
    pthread_rwlock_t rwlock;//定义读写锁对象
    int pthread_rwlock_init(pthread_rwlock_t* rwlock,
    				 const pthread_rwlockattr_t* attr);// 初始化读写锁
    int pthread_rwlock_destroy(pthread_rwlock_t* rwlock);// 销毁读写锁
    
  • 申请读锁的API

    /*
    	读锁是打开的,加锁锁定读操作,写操作加锁久会阻塞
    	读锁可以重复加锁
    */
    int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);//加读锁
    //尝试加锁,失败返回错误号EBUSY
    int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock);
    //超时加锁,超时返回错误号ETIMEOUT
    int pthread_rwlock_timedrdlock(pthread_rwlock_t* rwlock, 
    						const struct timespec* abstime);
    
  • 申请写锁的API

    // 如果读写锁没有被加锁,则可以加写锁,如果被了读或者写锁,直接上锁
    int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);//加写锁
    int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock);//尝试加锁
    int pthread_rwlock_timedwrlock(pthread_rwlock_t* rwlock, 
    							const struct timespec* abstime);//超时加锁
    
  • 解锁(读锁和写锁都是一个接口):

    int pthread_rwlock_unlock (pthread_rwlock_t* rwlock);
    
  • 读写锁属性设置API:

#include <pthread.h>
// 定义属性对象
pthread_rwlockattr_t attr;
// 初始化属性变量
int pthread_rwlockattr_init(pthread_rwlockattr_t* attr);
// 销毁属性变量
int pthread_rwlockattr_destroy(pthread_rwlockattr_t* attr);
// 设置属性
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t* attr, int pref);
// 查询属性
int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t* attr, int* pref);
// pref可取值
enum
{
    //读者优先(即同时请求读锁和写锁时,请求读锁的线程优先获得锁)
    PTHREAD_RWLOCK_PREFER_READER_NP, 
    //不要被名字所迷惑,也是读者优先
    PTHREAD_RWLOCK_PREFER_WRITER_NP, 
    //写者优先(即同时请求读锁和写锁时,请求写锁的线程优先获得锁)
    PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP,                 
    PTHREAD_RWLOCK_DEFAULT_NP = PTHREAD_RWLOCK_PREFER_READER_NP
};
  • TIP:

    • 如果当前读写锁被加读锁,则其他线程可以继续请求读锁,而请求写锁的都会被阻塞
    • 如果当前读写锁被加写锁,则其他线程请求的读操作和写操作都会阻塞
    • 如果使用读锁,可能需要考虑临界区的原子性,比如cout多线程可能会串掉
    • 读写锁默认属性是读锁优先,可以通过设置读写锁属性,设置写锁优先
  • 读写锁,写锁优先实列:

    #include <pthread.h>
    #include <unistd.h>
    #include <iostream>
    
    int resourceID = 0;
    pthread_rwlock_t myrwlock;
    
    void* read_thread(void* param)
    {    
        while (true)
        {
            //请求读锁
            pthread_rwlock_rdlock(&myrwlock);
    
            std::cout << "read thread ID: " << pthread_self() 
            			<< ", resourceID: " << resourceID << std::endl;
    
            //使用睡眠模拟读线程读的过程消耗了很久的时间
            sleep(1);
    
            pthread_rwlock_unlock(&myrwlock);
        }
    
        return NULL;
    }
    
    void* write_thread(void* param)
    {
        while (true)
        {
            //请求写锁
            pthread_rwlock_wrlock(&myrwlock);
    
            ++resourceID;
            std::cout << "write thread ID: " << pthread_self() 
            			<< ", resourceID: " << resourceID << std::endl;
    
            pthread_rwlock_unlock(&myrwlock);
    
            //放在这里增加请求读锁线程获得锁的几率
            sleep(1);
        }
    
        return NULL;
    }
    
    int main()
    {
        pthread_rwlockattr_t attr;
        pthread_rwlockattr_init(&attr);
        //设置成请求写锁优先
        pthread_rwlockattr_setkind_np(&attr, 
        						PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
        pthread_rwlock_init(&myrwlock, &attr);
    
        //创建5个请求读锁线程
        pthread_t readThreadID[5];
        for (int i = 0; i < 5; ++i)
        {
            pthread_create(&readThreadID[i], NULL, read_thread, NULL);
        }
    
        //创建一个请求写锁线程
        pthread_t writeThreadID;
        pthread_create(&writeThreadID, NULL, write_thread, NULL);
    
        pthread_join(writeThreadID, NULL);
    
        for (int i = 0; i < 5; ++i)
        {
            pthread_join(readThreadID[i], NULL);
        }
    
        pthread_rwlock_destroy(&myrwlock);
    
        return 0;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值