互斥锁的概念和使用
- 线程通信-互斥
临界资源:一次只允许一个任务(进程、线程)访问的共享资源
临界区:访问临界资源的代码
互斥机制:mutex互斥锁,任务访问临界资源前申请锁,访问完后释放锁 - 互斥锁初始化
两种方法创建互斥锁,静态方式和动态方式
动态方式:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
其中mutexattr用于指定互斥锁属性,如果为NULL则使用缺省属性。
静态方式:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - 锁的销毁:
int pthread_mutex_destroy(pthread_mutex_t *mutex)
在Linux中,互斥锁并不占用任何资源,因此LinuxThreads中的 pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。 - 互斥锁的使用
int pthread_mutex_lock(pthread_mutex_t *mutex)//mutex 指的是互斥锁对象
int pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
注:
pthread_mutex_lock 如果无法获取锁,任务阻塞,pthread_mutex_trylock如果无法获取锁,返回EBUSY而不是挂起等待
vim 设置代码全文格式化:gg=G - 代码如下:
- 执行如下:
注:可以发现文件里的字符很乱,因为线程之间没有锁,都在一起执行,就会处乱,所以需要加入互斥锁的概念
读写锁概念和使用
- 读写锁
必要性:提高线程执行效率
特性:
写者:写者使用写锁,如果当前没有读者,也没有其他写者,写者立即获得写锁;否则写者将等待,直到没有读者和写者。
读者:读者使用读锁,如果当前没有写者,读者立即获得读锁;否则读者等待,直到没有写者。
注意:
同一时刻只有一个线程可以获得写锁,同一时刻可以有多个线程获得读锁。
读写锁出于写锁状态时,所有试图对读写锁加锁的线程,不管是读者试图加读锁,还是写者试图加写锁,都会被阻塞。
读写锁处于读锁状态时,有写者试图加写锁时,之后的其他线程的读锁请求会被阻塞,以避免写者长时间的不写锁
初始化一个读写锁 pthread_rwlock_init
读锁定读写锁 pthread_rwlock_rdlock
非阻塞读锁定 pthread_rwlock_tryrdlock
写锁定读写锁 pthread_rwlock_wrlock
非阻塞写锁定 pthread_rwlock_trywrlock
解锁读写锁 pthread_rwlock_unlock
释放读写锁 pthread_rwlock_destroy - 写锁代码如下:
在main函数中加入init函数即可,执行后发现可以运行 - 读锁代码如下:
- 执行如下:
注:可以得出,这两个线程同时在读一个文件,还要注意读指令结束后,才可以写入,但如果在没有读完文件时,就退出,则文件中写不进去信息
死锁的避免
- 概念:
线程1有一个把锁,且拥有资源1,线程2有一把锁,且拥有资源2,但是线程1想获得资源2,线程2想获得资源1,因此产生互斥锁 - 避免方法:
锁越少越好,最好使用一把锁
调整好锁的顺序 - 代码如下:
- 执行如下:
在ps 下线程如下:
注:可以得出两个线程谁也拿不到谁的东西,直接卡死在上述的位置,线程1想获取锁2的东西卡死,反之亦然 - 可以加入sleep函数来避免这个问题如下:
- 问题解决执行如下: