互斥锁(互斥量)
描述
一个进程下的线程是共享资源的,通信便利的同时也造成了许多麻烦,线程程和线程之间如果同时访问一块资源就会出错,所以要引入一个互斥变量给它加锁,让它去协同不同线程程之间的访问(线程同步)
pthread_mutex_init函数
函数描述
初始化一个互斥量
函数原型
int pthread_mutex_init(pthread_mutex_t *mtx,const pthread_mutexattr *attr)
参一:传出参数,互斥量
参二:互斥量属性的指针
pthread_mutex_lock函数
阻塞加锁,如果没有加锁就加锁,有锁的话阻塞等待锁的消失(对于互斥量而言)
int pthread_mutex_lock(pthread_mutex_t *mtx)
pthread_mutex_unlock函数
int pthread_mutex_unlock(pthread_mutex_t *mtx)
pthread_mutex_destroy函数
int pthread_mutex_destroy(pthread_mutex_t *mtx)
# include <stdio.h>
# include <string.h>
# include <unistd.h>
# include <pthread.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <stdlib.h>
const int N= 1e6 ;
int a;
pthread_mutex_t mtx;
void * thread1 ( void * arg) {
for ( int i = 1 ; i <= N; i++ ) {
pthread_mutex_lock ( & mtx) ;
a++ ;
pthread_mutex_unlock ( & mtx) ;
}
}
int main ( int argc, char * argv[ ] )
{
pthread_t tids[ 10 ] ;
pthread_mutex_init ( & mtx, NULL ) ;
for ( int i = 0 ; i < 10 ; i++ ) {
pthread_create ( tids+ i, NULL , thread1, NULL ) ;
}
for ( int i = 0 ; i < 10 ; i++ ) {
pthread_join ( tids[ i] , NULL ) ;
}
printf ( "a = %d\n" , a) ;
pthread_mutex_destroy ( & mtx) ;
return 0 ;
}
pthread_mutex_trylock函数
非阻塞加锁(可以解决死锁问题)
练习
# include <stdio.h>
# include <string.h>
# include <unistd.h>
# include <pthread.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <stdlib.h>
# include <time.h>
int seat[ 11 ] , seat_num= 10 , thread_num= 12 ;
pthread_t thread[ 13 ] ;
pthread_mutex_t mtx;
int get_id ( ) {
int r_id, res;
if ( seat_num== 0 ) return 0 ;
r_id= rand ( ) % seat_num+ 1 ;
res= seat[ r_id] ;
seat[ r_id] = seat[ seat_num] ;
seat_num-- ;
return res;
}
void * thread1 ( void * arg) {
pthread_mutex_lock ( & mtx) ;
int seat_id= get_id ( ) ;
pthread_mutex_unlock ( & mtx) ;
if ( seat_id== 0 ) {
printf ( "用户 %d 选座失败,座位已售空\n" , * ( int * ) arg) ;
} else {
printf ( "用户 %d 成功选得座位 %d\n" , * ( int * ) arg, seat_id) ;
}
return NULL ;
}
int main ( int argc, char * argv[ ] )
{
srand ( ( unsigned ) time ( 0 ) ) ;
for ( int i= 1 ; i<= 10 ; i++ ) {
seat[ i] = i;
}
pthread_mutex_init ( & mtx, NULL ) ;
for ( int i= 1 ; i<= 12 ; i++ ) {
int * user_id= ( int * ) malloc ( sizeof ( int ) ) ;
* user_id= i;
pthread_create ( & thread[ i] , NULL , thread1, user_id) ;
}
for ( int i= 1 ; i<= 12 ; i++ ) {
pthread_join ( thread[ i] , NULL ) ;
}
pthread_mutex_destroy ( & mtx) ;
return 0 ;
}
死锁现象
描述
当多个线程为了保护多个共享资源使用了多个互斥锁,如果使用不恰当,就可能造成多个线程之间一直阻塞等待对方锁的释放,这种现象叫死锁
具体例子
现在又俩个线程,A线程和B线程,现在同时运行,A线程给互斥量1上了锁,B线程给互斥量2上了锁,然后sleep了1s,此时A线程持有互斥量1并等待B线程互斥量2解锁,B线程中持有互斥量2并等待A线程互斥量1解锁,AB线程互相等待,产生死锁
# include <stdio.h>
# include <string.h>
# include <unistd.h>
# include <pthread.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <stdlib.h>
# include <time.h>
pthread_mutex_t mtx1, mtx2;
void * thread1 ( void * arg) {
printf ( "child get mtx1.........\n" ) ;
pthread_mutex_lock ( & mtx1) ;
printf ( "child get mtx1 success!\n" ) ;
sleep ( 1 ) ;
printf ( "child get mtx2.........\n" ) ;
pthread_mutex_lock ( & mtx2) ;
printf ( "child get mtx2 success!\n" ) ;
pthread_mutex_unlock ( & mtx1) ;
pthread_mutex_unlock ( & mtx2) ;
}
int main ( int argc, char * argv[ ] )
{
pthread_mutex_init ( & mtx1, NULL ) ;
pthread_mutex_init ( & mtx2, NULL ) ;
pthread_t tid;
pthread_create ( & tid, NULL , thread1, NULL ) ;
printf ( "main get mtx2.........\n" ) ;
pthread_mutex_lock ( & mtx2) ;
printf ( "main get mtx2 success!\n" ) ;
sleep ( 1 ) ;
printf ( "main get mtx1.........\n" ) ;
pthread_mutex_lock ( & mtx1) ;
printf ( "main get mtx1 success!\n" ) ;
pthread_mutex_unlock ( & mtx2) ;
pthread_mutex_unlock ( & mtx1) ;
return 0 ;
}
死锁产生的四个必要条件
1.互斥条件:必须得有多个互斥变量
2.持有并等待条件:A持有锁1并等待B锁2解锁,B持有锁2并等待A锁1解锁
3.不可剥夺条件:必须一直阻塞
4.环路等待条件
避免死锁
1.不加锁(不太现实奥)
2.锁的力度控制:尽量减少持有锁的事件,降低死锁发生概率
3.资源有序分配:破坏环路条件1 2 1 2
4.重试机制:破坏不可剥夺条件,一般为非阻塞的trylock
# include <stdio.h>
# include <string.h>
# include <unistd.h>
# include <pthread.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <stdlib.h>
# include <time.h>
# include <errno.h>
pthread_mutex_t mtx1, mtx2;
void * thread1 ( void * arg) {
while ( 1 ) {
printf ( "child get mtx1.........\n" ) ;
pthread_mutex_lock ( & mtx1) ;
printf ( "child get mtx1 success!\n" ) ;
sleep ( 1 ) ;
printf ( "child get mtx2.........\n" ) ;
int ret = pthread_mutex_trylock ( & mtx2) ;
if ( ret != 0 && ret == EBUSY) {
pthread_mutex_unlock ( & mtx1) ;
printf ( "child free mtx1\n" ) ;
continue ;
}
printf ( "child get mtx2 success!\n" ) ;
pthread_mutex_unlock ( & mtx1) ;
pthread_mutex_unlock ( & mtx2) ;
return NULL ;
}
}
int main ( int argc, char * argv[ ] )
{
pthread_mutex_init ( & mtx1, NULL ) ;
pthread_mutex_init ( & mtx2, NULL ) ;
pthread_t tid;
pthread_create ( & tid, NULL , thread1, NULL ) ;
printf ( "main get mtx2.........\n" ) ;
pthread_mutex_lock ( & mtx2) ;
printf ( "main get mtx2 success!\n" ) ;
sleep ( 1 ) ;
printf ( "main get mtx1.........\n" ) ;
pthread_mutex_lock ( & mtx1) ;
printf ( "main get mtx1 success!\n" ) ;
pthread_mutex_unlock ( & mtx2) ;
pthread_mutex_unlock ( & mtx1) ;
pthread_exit ( NULL ) ;
return 0 ;
}
读写锁
已经有互斥锁了为什么还要有读写锁?
当多个线程读多写少的时候建议使用读写锁
概述
读写锁由读锁和写锁两部分构成,特性为写独占,读共享。
适用于写多读少的情况
特性
写独占,读共享
一个线程写的时候,另一个线程不能写
一个线程读的时候,另一个线程可以读
读写锁函数
场景分析
持有读锁时,申请读锁,加锁成功,不需要等待
持有写锁时,申请写锁,会阻塞等待写锁的解锁,然后再加锁
持有读锁时,申请写锁,会阻塞等待读锁的解锁,然后再加锁
持有写锁时,申请读锁,会阻塞等带写锁的写锁,然后再加锁
持有读锁时,申请写锁和读锁,读锁会加锁成功,写锁会阻塞等待(如果是很多的读锁和少量的写锁的话,写锁会一直无法请求成功,造成饥饿)
持有写锁时,申请写锁和读锁,(如果是读优先)读锁会加锁成功,写锁阻塞等待写锁解锁,然后再加锁
# include <stdio.h>
# include <string.h>
# include <unistd.h>
# include <pthread.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <stdlib.h>
# include <time.h>
# include <errno.h>
pthread_rwlock_t rwlock;
void * thread1 ( void * arg) {
pthread_rwlock_wrlock ( & rwlock) ;
printf ( " thread1 加锁成功\n" ) ;
sleep ( 2 ) ;
pthread_rwlock_unlock ( & rwlock) ;
printf ( "thread1 解锁\n" ) ;
}
void * thread2 ( void * arg) {
pthread_rwlock_wrlock ( & rwlock) ;
printf ( " thread2 加锁成功\n" ) ;
sleep ( 2 ) ;
pthread_rwlock_unlock ( & rwlock) ;
printf ( "thread2 解锁\n" ) ;
}
void * thread3 ( void * arg) {
pthread_rwlock_rdlock ( & rwlock) ;
printf ( " thread3 加锁成功\n" ) ;
sleep ( 2 ) ;
pthread_rwlock_unlock ( & rwlock) ;
printf ( "thread3 解锁\n" ) ;
}
int main ( int argc, char * argv[ ] )
{
pthread_rwlock_init ( & rwlock, NULL ) ;
pthread_t tid;
pthread_create ( & tid, NULL , thread1, NULL ) ;
sleep ( 1 ) ;
pthread_create ( & tid, NULL , thread2, NULL ) ;
sleep ( 1 ) ;
pthread_create ( & tid, NULL , thread3, NULL ) ;
pthread_exit ( NULL ) ;
return 0 ;
}
练习
# include <stdio.h>
# include <string.h>
# include <unistd.h>
# include <pthread.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <stdlib.h>
# include <time.h>
# include <errno.h>
pthread_rwlock_t rwlock;
int balance;
void * save ( void * arg) {
int val = atoi ( ( char * ) arg) ;
pthread_rwlock_wrlock ( & rwlock) ;
sleep ( 1 ) ;
balance+= val;
printf ( "save money:%d , balance:%d\n" , val, balance) ;
pthread_rwlock_unlock ( & rwlock) ;
}
void * query ( void * arg) {
pthread_rwlock_rdlock ( & rwlock) ;
int r = rand ( ) % 3 + 1 ;
sleep ( r) ;
printf ( "query : balance : %d\n" , balance) ;
pthread_rwlock_unlock ( & rwlock) ;
}
int main ( int argc, char * argv[ ] )
{
srand ( ( unsigned ) time ( 0 ) ) ;
pthread_t threads[ 15 ] ;
pthread_rwlock_init ( & rwlock, NULL ) ;
for ( int i= 0 ; i< 5 ; i++ ) {
pthread_create ( & threads[ i] , NULL , save, argv[ i+ 1 ] ) ;
}
for ( int i= 5 ; i< 15 ; i++ ) {
pthread_create ( & threads[ i] , NULL , query, NULL ) ;
}
for ( int i= 0 ; i< 15 ; i++ ) {
pthread_join ( threads[ i] , NULL ) ;
}
pthread_rwlock_destroy ( & rwlock) ;
return 0 ;
}
条件变量
条件变量用于进程同步当中。条件变量允许一个线程阻塞等待某个条件的成立,改线程会被挂起,不占用cpu资源。当条件满足的时候,可以唤醒阻塞等待的线程继续执行
条件变量一般与互斥锁一同使用
函数
int pthread_cond_init ( pthread_cond_t * cond, pthread_condattr_t * cond_attr) ;
int pthread_cond_destroy ( pthread_cond_t * cond)
int pthread_cond_wait ( pthread_cond_t * t)
# include <stdio.h>
# include <string.h>
# include <unistd.h>
# include <pthread.h>
# include <fcntl.h>
int a;
pthread_mutex_t mtx;
pthread_cond_t cond;
void * thread1 ( void * arg) {
for ( int i= 0 ; i< 10 ; i++ ) {
pthread_mutex_lock ( & mtx) ;
while ( a != 0 )
{
pthread_cond_wait ( & cond, & mtx) ;
}
printf ( "a = %d\n" , ++ a) ;
pthread_cond_broadcast ( & cond) ;
pthread_mutex_unlock ( & mtx) ;
}
}
void * thread2 ( void * arg) {
for ( int i= 0 ; i< 10 ; i++ ) {
pthread_mutex_lock ( & mtx) ;
while ( a != 1 )
{
pthread_cond_wait ( & cond, & mtx) ;
}
printf ( "a = %d\n" , -- a) ;
pthread_cond_broadcast ( & cond) ;
pthread_mutex_unlock ( & mtx) ;
}
}
int main ( int argc, char * argv[ ] )
{
pthread_mutex_init ( & mtx, NULL ) ;
pthread_t tid1, tid2, tid3, tid4;
pthread_create ( & tid1, NULL , thread1, NULL ) ;
pthread_create ( & tid2, NULL , thread2, NULL ) ;
pthread_create ( & tid3, NULL , thread1, NULL ) ;
pthread_create ( & tid4, NULL , thread2, NULL ) ;
pthread_join ( tid1, NULL ) ;
pthread_join ( tid2, NULL ) ;
pthread_join ( tid3, NULL ) ;
pthread_join ( tid4, NULL ) ;
return 0 ;
}
生产者消费者问题
# include <stdio.h>
# include <string.h>
# include <unistd.h>
# include <pthread.h>
# include <fcntl.h>
# include <time.h>
# include <stdlib.h>
pthread_mutex_t mtx;
pthread_cond_t cond;
struct node * head;
struct node {
int val;
struct node * next;
} ;
void add_val ( int val) {
struct node * p= ( struct node * ) malloc ( sizeof ( struct node ) ) ;
p-> val= val;
p-> next= head;
head= p;
}
int get_val ( ) {
if ( ! head) {
return - 1 ;
}
int ans= head-> val;
struct node * tmp= head;
head = head-> next;
free ( tmp) ;
return ans;
}
void * producer ( void * arg) {
while ( 1 ) {
pthread_mutex_lock ( & mtx) ;
add_val ( rand ( ) % 500 + 1 ) ;
pthread_cond_broadcast ( & cond) ;
pthread_mutex_unlock ( & mtx) ;
}
}
void * conmuser ( void * arg) {
while ( 1 ) {
pthread_mutex_lock ( & mtx) ;
int num= get_val ( ) ;
if ( num == - 1 ) {
pthread_cond_wait ( & cond, & mtx) ;
}
printf ( "num = %d\n" , num) ;
sleep ( 1 ) ;
pthread_mutex_unlock ( & mtx) ;
}
}
int main ( int argc, char * argv[ ] )
{
srand ( time ( 0 ) ) ;
pthread_mutex_init ( & mtx, NULL ) ;
pthread_cond_init ( & cond, NULL ) ;
pthread_t tid1, tid2, tid3, tid4;
pthread_create ( & tid1, NULL , producer, NULL ) ;
pthread_create ( & tid2, NULL , conmuser, NULL ) ;
pthread_join ( tid1, NULL ) ;
pthread_join ( tid2, NULL ) ;
return 0 ;
}
信号量
两个线程堆a自增1万次
# include <stdio.h>
# include <string.h>
# include <unistd.h>
# include <pthread.h>
# include <fcntl.h>
# include <semaphore.h>
int a;
sem_t sem;
void * thread1 ( void * arg) {
for ( int i= 0 ; i< 100000 ; i++ ) {
sem_wait ( & sem) ;
a++ ;
sem_post ( & sem) ;
}
}
int main ( int argc, char * argv[ ] )
{
sem_init ( & sem, 0 , 1 ) ;
pthread_t tid1, tid2;
pthread_create ( & tid1, NULL , thread1, NULL ) ;
pthread_create ( & tid2, NULL , thread1, NULL ) ;
pthread_join ( tid1, NULL ) ;
pthread_join ( tid2, NULL ) ;
printf ( "a = %d\n" , a) ;
return 0 ;
}