系统编程七:线程同步互斥+读写锁+条件变量

一、验证当前一个进程中最多只能开多少条线程?

#include<stdio.h>
#include <pthread.h>

void* start_routine(void *arg)
{
	
	
}
int main()
{
	int i=0;
	
	while(++i)
	{
		pthread_t thread;
		int ret = pthread_create(&thread,NULL,start_routine,NULL);
		if( ret != 0 )
		{
			printf("error:%d\n",i);
			break;
		}
		pthread_join(thread,NULL);

		printf("i:%d\n",i);
	}
}

二、问题的引入

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>


int g_val = 0;
//1)定义互斥锁变量。 -----》数据类型  pthread_mutex_t 
pthread_mutex_t mutex;
	
//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine1(void *arg)
{
	pthread_mutex_lock(&mutex);//上锁
	//写操作,修改内存空间的值 
	g_val = 100;
	int i;

	for(i=0; i<5; i++)
	{
		sleep(1);	
		g_val += g_val*i;
		printf("%d  routine1 100  g_val:%d\n",i,g_val);		
	}

	pthread_mutex_unlock(&mutex);//解锁
}

//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine2(void *arg)
{
	pthread_mutex_lock(&mutex);//上锁
	//写操作,修改内存空间的值 
	g_val = 200;
	int i;
	
	for(i=0; i<5; i++)
	{
		sleep(1);	
		g_val += g_val*i;	
		printf("routine2 200  g_val:%d\n",g_val);		
	}

	pthread_mutex_unlock(&mutex);//解锁
}

//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine3(void *arg)
{
	//pthread_mutex_lock(&mutex);//上锁
	int i;
	//读操作,此时仅仅只是将这个值打印出来
	for(i=0; i<5; i++)
	{
		sleep(1);		
		printf("routine3  g_val:%d\n",g_val);		
	}

	//pthread_mutex_unlock(&mutex);//解锁
}
//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine4(void *arg)
{
	//pthread_mutex_lock(&mutex);//上锁
	int i;
	//读操作,此时仅仅只是将这个值打印出来
	for(i=0; i<5; i++)
	{
		sleep(1);		
		printf("routine4  g_val:%d\n",g_val);		
	}

	//pthread_mutex_unlock(&mutex);//解锁
}

int main()
{
	//2)初始化 互斥锁
	 pthread_mutex_init(&mutex,NULL);
	  
	// 创建一个新的线程1
	pthread_t thread1;	
	pthread_create(&thread1,NULL,routine1,NULL);					  
	// 创建一个新的线程2
	pthread_t thread2;	
	pthread_create(&thread2,NULL,routine2,NULL);	
	// 创建一个新的线程3
	pthread_t thread3;	
	pthread_create(&thread3,NULL,routine3,NULL);					  
	// 创建一个新的线程4
	pthread_t thread4;	
	pthread_create(&thread4,NULL,routine4,NULL);	

	
	//接合子线程 --阻塞等待子线程退出 回收资源
	pthread_join(thread1,NULL);
	pthread_join(thread2,NULL);
	//pthread_join(thread3,NULL);
	
	//5)销毁互斥锁
	pthread_mutex_destroy(&mutex);
	return 0;
}

想要实现的效果:为了提高效率,有没有什么方法 可以 让两条线程(线程3 线程4)在进行读操作,可以同时进行??
而且要保证在进行读操作不可以进行写操作。

----互斥锁 没有办法 能够直接实现, 可以使用 读写锁。

三、读写锁

1、互斥锁的缺点

        互斥锁无论是读取共享资源,还是修改共享资源,都要上锁,而且在上锁期间,不能被别的线程上锁。

访问资源(读取)----》同时上读锁 ----》读锁就是一把共享锁。同时上读锁的线程可以并发执行 
修改资源(写入)----》不能同时上写锁--》写锁 就是 一把互斥锁。比如说 有多个线程中 上了 写锁,此时 只能执行其中一个带有写锁的线程。

        多个同时挂了 读锁的线程 可以并发执行,注意 挂了读锁的线程 与 挂了写锁的线程是互斥的。写锁的线程之间也是互斥的,也就是说,当前有一个写锁的线程正在执行,另一个拥有写锁的线程会阻塞。

2、读写锁的函数接口

1)定义一个读写锁变量 ---数据类型 pthread_rwlock_t
    pthread_rwlock_t rwlock;
    
2)初始化读写锁 ---》pthread_rwlock_init
NAME
       initialize a read-write lock object
    
SYNOPSIS
       #include <pthread.h>

       int pthread_rwlock_init(pthread_rwlock_t * rwlock,const pthread_rwlockattr_t * attr);

参数: 
        rwlock:读写锁变量的地址 
        attr:属性 ,默认属性 为 NULL 
        
返回值: 
        成功返回  0
        失败  返回 错误码
        
3)读锁上锁 ----pthread_rwlock_rdlock

    #include <pthread.h>
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

参数: 
        rwlock:读写锁变量的地址 

返回值: 
        成功返回  0
        失败  返回 错误码

4)写锁上锁 
       #include <pthread.h>
       int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
参数: 
        rwlock:读写锁变量的地址 

返回值: 
        成功返回  0
        失败  返回 错误码

5)读写锁 解锁 ----》pthread_rwlock_unlock

    #include <pthread.h>
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

参数: 
        rwlock:读写锁变量的地址 

返回值: 
        成功返回  0
        失败  返回 错误码    
    
6)销毁读写锁-----》pthread_rwlock_destroy

    #include <pthread.h>
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    
参数: 
        rwlock:读写锁变量的地址 

返回值: 
        成功返回  0
        失败  返回 错误码    

3、练习1

        使用 读写锁 修改     02线程使用互斥锁实现同步互斥.c --》让 读操作的线程可以并发执行,并且  跟 写操作的线程 达到互斥的关系,提高效率。

//02线程使用互斥锁实现同步互斥.c
#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>


int g_val = 0;
//1)定义互斥锁变量。 -----》数据类型  pthread_mutex_t 
pthread_mutex_t mutex;
	
//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine1(void *arg)
{
	pthread_mutex_lock(&mutex);//上锁
	//写操作,修改内存空间的值 
	g_val = 100;
	int i;

	for(i=0; i<5; i++)
	{
		sleep(1);	
		g_val += g_val*i;
		printf("%d  routine1 100  g_val:%d\n",i,g_val);		
	}

	pthread_mutex_unlock(&mutex);//解锁
}

//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine2(void *arg)
{
	pthread_mutex_lock(&mutex);//上锁
	//写操作,修改内存空间的值 
	g_val = 200;
	int i;
	
	for(i=0; i<5; i++)
	{
		sleep(1);	
		g_val += g_val*i;	
		printf("routine2 200  g_val:%d\n",g_val);		
	}

	pthread_mutex_unlock(&mutex);//解锁
}

//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine3(void *arg)
{
	//pthread_mutex_lock(&mutex);//上锁
	int i;
	//读操作,此时仅仅只是将这个值打印出来
	for(i=0; i<5; i++)
	{
		sleep(1);		
		printf("routine3  g_val:%d\n",g_val);		
	}

	//pthread_mutex_unlock(&mutex);//解锁
}
//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine4(void *arg)
{
	//pthread_mutex_lock(&mutex);//上锁
	int i;
	//读操作,此时仅仅只是将这个值打印出来
	for(i=0; i<5; i++)
	{
		sleep(1);		
		printf("routine4  g_val:%d\n",g_val);		
	}

	//pthread_mutex_unlock(&mutex);//解锁
}

int main()
{
	//2)初始化 互斥锁
	 pthread_mutex_init(&mutex,NULL);
	  
	// 创建一个新的线程1
	pthread_t thread1;	
	pthread_create(&thread1,NULL,routine1,NULL);					  
	// 创建一个新的线程2
	pthread_t thread2;	
	pthread_create(&thread2,NULL,routine2,NULL);	
	// 创建一个新的线程3
	pthread_t thread3;	
	pthread_create(&thread3,NULL,routine3,NULL);					  
	// 创建一个新的线程4
	pthread_t thread4;	
	pthread_create(&thread4,NULL,routine4,NULL);	

	
	//接合子线程 --阻塞等待子线程退出 回收资源
	pthread_join(thread1,NULL);
	pthread_join(thread2,NULL);
	//pthread_join(thread3,NULL);
	
	//5)销毁互斥锁
	pthread_mutex_destroy(&mutex);
	return 0;
}

//03线程使用读写锁实现同步互斥.c
#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>


int g_val = 10;
//1)定义一个读写锁变量 ---数据类型 pthread_rwlock_t
pthread_rwlock_t rwlock;
	
//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine1(void *arg)
{
	pthread_rwlock_wrlock(&rwlock);//写锁上锁 
	//写操作,修改内存空间的值 
	g_val = 100;
	int i;

	for(i=0; i<5; i++)
	{
		sleep(1);	
		g_val += g_val*i;
		printf("%d  routine1 100  g_val:%d\n",i,g_val);		
	}

	pthread_rwlock_unlock(&rwlock);//读写锁 解锁
}

//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine2(void *arg)
{
	pthread_rwlock_wrlock(&rwlock);//写锁上锁 
	//写操作,修改内存空间的值 
	g_val = 200;
	int i;
	
	for(i=0; i<5; i++)
	{
		sleep(1);	
		g_val += g_val*i;	
		printf("routine2 200  g_val:%d\n",g_val);		
	}

	pthread_rwlock_unlock(&rwlock);//读写锁 解锁
}

//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine3(void *arg)
{
	pthread_rwlock_rdlock(&rwlock);//读锁上锁
	int i;
	//读操作,此时仅仅只是将这个值打印出来
	for(i=0; i<5; i++)
	{
		sleep(1);		
		printf("routine3  g_val:%d\n",g_val);		
	}

	pthread_rwlock_unlock(&rwlock);//读写锁 解锁
}
//线程的例程函数,也就是创建线程之后,去执行这个函数
void* routine4(void *arg)
{
	pthread_rwlock_rdlock(&rwlock);//读锁上锁
	int i;
	//读操作,此时仅仅只是将这个值打印出来
	for(i=0; i<5; i++)
	{
		sleep(1);		
		printf("routine4  g_val:%d\n",g_val);		
	}

	pthread_rwlock_unlock(&rwlock);//读写锁 解锁
}

int main()
{
	//2)初始化读写锁
	pthread_rwlock_init(&rwlock,NULL);
	  
	// 创建一个新的线程1
	pthread_t thread1;	
	pthread_create(&thread1,NULL,routine1,NULL);					  
	// 创建一个新的线程2
	pthread_t thread2;	
	pthread_create(&thread2,NULL,routine2,NULL);	
	// 创建一个新的线程3
	pthread_t thread3;	
	pthread_create(&thread3,NULL,routine3,NULL);					  
	// 创建一个新的线程4
	pthread_t thread4;	
	pthread_create(&thread4,NULL,routine4,NULL);	

	
	//接合子线程 --阻塞等待子线程退出 回收资源
	pthread_join(thread1,NULL);
	pthread_join(thread2,NULL);
	pthread_join(thread3,NULL);
	pthread_join(thread4,NULL);
	
	//6)销毁读写锁
	pthread_rwlock_destroy(&rwlock);
	return 0;
}

4、练习2

        有一个进程,创建5个线程出来,每个线程任务都是一样的。

        任务:将 "helloworld"字符串每隔1S打印一个字符。--》任务时间:10S

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>

/*
练习2:有一个进程,创建5个线程出来,每个线程任务都是一样的。
		任务:将 "helloworld"字符串每隔1S打印一个字符。--》任务时间:10S

*/

//线程的例程函数
void* routine(void*arg)
{
	char str[] = "helloworld";
	char *p = str; 
	while(*p != '\0')
	{
		printf("%c\n",*p);
		sleep(1);
		p++;
	}
	//主动退出
	pthread_exit(NULL);
}

int main()
{
	int i;
	pthread_t thead[5];
	
	for(i=0; i<5; i++)
	{	
		pthread_create(&thead[i],NULL,routine,NULL);	
	}
	
	for(i=0; i<5; i++)
	{
		pthread_join(thead[i],NULL);
	}
	
	return 0;
}

5、练习3

        有一个进程,创建5个线程出来,每个线程任务都是一样的。

        任务:将 "helloworld"字符串每隔1S打印一个字符,处理任务的时候使用互斥锁。--》任务时间:10S

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>

/*
练习3: 有一个进程,创建5个线程出来,每个线程任务都是一样的。
		任务:将 "helloworld"字符串每隔1S打印一个字符,处理任务的时候使用互斥锁。--》任务时间:10S

*/

//1、定义一把互斥锁
pthread_mutex_t mutex;

//线程的例程函数
void* routine(void*arg)
{	
	pthread_mutex_lock(&mutex);//上锁
	
	char str[] = "helloworld";
	char *p = str; 
	while(*p != '\0')
	{
		printf("%c\n",*p);
		sleep(1);
		p++;
	}
	pthread_mutex_unlock(&mutex);//解锁
	
	//主动退出
	pthread_exit(NULL);
}

int main()
{
	int i;
	pthread_t thead[5];
	
	//2、初始化互斥锁
	pthread_mutex_init(&mutex,NULL);
	
	for(i=0; i<5; i++)
	{	
		pthread_create(&thead[i],NULL,routine,NULL);	
	}
	
	
	for(i=0; i<5; i++)
	{
		pthread_join(thead[i],NULL);
	}
	
	
	//销毁互斥锁
	pthread_mutex_destroy(&mutex);
	
	return 0;
}

6、练习4

        有一个进程,创建5个线程出来,让五个线程分别打印字符串"hello"中的一个字母就退出,
并且保证打印出来的字符串的顺序不能变。

比如:  
        线程1 ----》打印 'h' ----退出 
        线程2 ----》打印 'e' ----退出
        线程3 ----》打印 'l' ----退出
        线程4 ----》打印 'l' ----退出
        线程5 ----》打印 'o' ----退出

以前 :

int main()
{
    int i;
    char str[] = "hello"
    for(i=0; i<strlen(str); i++)
    {
        printf("%c\n",str[i]);
    }
}


现在使用线程: 

//线程的例程函数
void* routine(void*arg)
{    
    pthread_mutex_lock(&mutex);//上锁
    
    printf("%c\n",str[myindex++]);

    pthread_mutex_unlock(&mutex);//解锁
    
    //主动退出
    pthread_exit(NULL);
}

全部代码:

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>

/*
练习4:有一个进程,创建5个线程出来,让五个线程分别打印字符串"hello"中的一个字母就退出,
并且保证打印出来的字符串的顺序不能变。

比如:  
		线程1 ----》打印 'h' ----退出 
		线程2 ----》打印 'e' ----退出
		线程3 ----》打印 'l' ----退出
		线程4 ----》打印 'l' ----退出
		线程5 ----》打印 'o' ----退出

*/

//1、定义一把互斥锁
pthread_mutex_t mutex;

char str[] = "hello";
int myindex;

//线程的例程函数
void* routine(void*arg)
{	
	pthread_mutex_lock(&mutex);//上锁
	
	printf("%c\n",str[myindex++]);

	pthread_mutex_unlock(&mutex);//解锁
	
	//主动退出
	pthread_exit(NULL);
}

int main()
{
	int i;
	pthread_t thead[5];
	
	//2、初始化互斥锁
	pthread_mutex_init(&mutex,NULL);
	
	for(i=0; i<5; i++)
	{	
		pthread_create(&thead[i],NULL,routine,NULL);	
	}
	
	
	for(i=0; i<5; i++)
	{
		pthread_join(thead[i],NULL);
	}
	
	
	//销毁互斥锁
	pthread_mutex_destroy(&mutex);
	
	return 0;
}

四、条件变量

1、什么是条件变量?

        线程因为某一个条件/情况不成立下,进入一个变量中等待,这个存放线程的变量就是条件变量。条件变量必须跟互斥锁一起使用。

2、关于条件变量的函数接口

1)定义一个条件变量。----》数据类型  pthread_cond_t
    pthread_cond_t cond;

2)初始化 条件变量  ---》pthread_cond_init    
    
    #include <pthread.h>
    int pthread_cond_init(pthread_cond_t * cond,const pthread_condattr_t * attr);
     
参数: 
        cond:条件变量的地址
        attr:属性 ,默认属性为NULL
返回值 :
        成功返回 0
        失败返回 错误码

    
3)进入条件变量中等待  

int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex);

参数: 
        cond:条件变量的地址
        mutex:互斥锁的地址 
        
返回值 :
        成功返回 0
        失败返回 错误码        
    
4)唤醒条件变量中等待的线程

全部唤醒(广播):pthread_cond_broadcast
随机唤醒一个(单播): pthread_cond_signal

    #include <pthread.h>

    int pthread_cond_broadcast(pthread_cond_t *cond);
    int pthread_cond_signal(pthread_cond_t *cond);

参数: 
        cond:条件变量的地址
        mutex:互斥锁的地址 
        
返回值 :
        成功返回 0
        失败返回 错误码        
    
5)销毁条件变量     

    #include <pthread.h>

    int pthread_cond_destroy(pthread_cond_t *cond);

参数: 
        cond:条件变量的地址

3、练习

        有4个小孩,每个小孩的任务就是领取生活费1000,回学校之前,先在银行卡中搞 2000块钱,2个线程退出,2个线程进去条件变量中等待,父亲再打钱1000,唤醒所有的小孩起来拿钱, 过一会,再打1000块钱,再唤醒最后一个小孩起来拿钱赶紧走人上学。

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>

/*
练习5:有4个小孩,每个小孩的任务就是领取生活费1000,回学校之前,先在银行卡中搞 2000块钱,2个线程退出,2个线程进去条件变量中等待,父亲再打钱1000,唤醒所有的小孩起来拿钱, 过一会,再打1000块钱,再唤醒最后一个小孩起来拿钱赶紧走人上学。

*/

//使用枚举定义 线程的各个状态
enum{
	PthreadReady,//表示线程准备就绪
	PthreadRun,//表示线程正在运行
	PthreadWait,//表示线程正在条件变量中等待
	PthreadEnd,//表示线程已经退出
};

//1、定义一把互斥锁
pthread_mutex_t mutex;
//1)定义一个条件变量
pthread_cond_t cond;

int money = 2000;//银行卡里面的初始值 --共享资源 --临界资源
int pthreadState1 = PthreadReady;//表示线程1的状态


//线程的例程函数
void* routine1(void*arg)
{	
	pthreadState1 = PthreadRun;
	pthread_mutex_lock(&mutex);//上锁
	
	//条件不满足的时候进入条件变量中等待
	while(money <1000)
	{
		pthreadState1 = PthreadWait;
		pthread_cond_wait(&cond,&mutex);//1 解锁 --阻塞等待
	}
	pthreadState1 = PthreadRun;
	money-=1000;
	printf("routine1 拿到钱了,当前银行卡余额:%d\n",money);
	
	pthread_mutex_unlock(&mutex);//解锁
	
	pthreadState1 = PthreadEnd;
	//主动退出 --走人
	pthread_exit(NULL);
}

//线程的例程函数
void* routine2(void*arg)
{	
	pthread_mutex_lock(&mutex);//上锁
	
	//条件不满足的时候进入条件变量中等待
	while(money <1000)
	{
		pthread_cond_wait(&cond,&mutex);//1 解锁 --阻塞等待
	}
	money-=1000;
	printf("routine2 拿到钱了,当前银行卡余额:%d\n",money);
	pthread_mutex_unlock(&mutex);//解锁
	
	//主动退出
	pthread_exit(NULL);
}
//线程的例程函数
void* routine3(void*arg)
{	
	pthread_mutex_lock(&mutex);//上锁
	
	//条件不满足的时候进入条件变量中等待
	while(money <1000)
	{
		pthread_cond_wait(&cond,&mutex);//1 解锁 --阻塞等待
	}
	money-=1000;
	printf("routine3 拿到钱了,当前银行卡余额:%d\n",money);
	pthread_mutex_unlock(&mutex);//解锁
	
	//主动退出
	pthread_exit(NULL);
}
//线程的例程函数
void* routine4(void*arg)
{	
	pthread_mutex_lock(&mutex);//上锁
	
	//条件不满足的时候进入条件变量中等待
	while(money <1000)
	{
		pthread_cond_wait(&cond,&mutex);//1 解锁 --阻塞等待 --被唤醒了
		//唤醒之后,往下面走
	}
	money-=1000;
	printf("routine4 拿到钱了,当前银行卡余额:%d\n",money);
	pthread_mutex_unlock(&mutex);//解锁
	
	//主动退出
	pthread_exit(NULL);
}
int main()
{
	//2、初始化互斥锁
	pthread_mutex_init(&mutex,NULL);
	//2)初始化 条件变量
	pthread_cond_init(&cond,NULL);
	
	
	pthread_t thead1;
	pthread_create(&thead1,NULL,routine1,NULL);	
	pthread_t thead2;
	pthread_create(&thead2,NULL,routine2,NULL);	
	pthread_t thead3;
	pthread_create(&thead3,NULL,routine3,NULL);	
	pthread_t thead4;
	pthread_create(&thead4,NULL,routine4,NULL);		
	
	//主线程
	int i;
	for(i=0; i<5; i++)//等待5s,之后,父亲过来打钱了
	{
		printf("当前延时%d\n",i);
		sleep(1);
	}
	printf("父亲第一次准备打钱了.....\n");
	//根据子线程的状态 在这里判断当前有没有线程在等待
	pthread_mutex_lock(&mutex);//上锁
	money +=1000;//打钱
	pthread_mutex_unlock(&mutex);//解锁
	//父亲拿着喇叭 全部唤醒 所有在条件变量中等待的孩子
	pthread_cond_broadcast(&cond);

	for(i=0; i<5; i++)//等待5s,之后,父亲过来打钱了
	{
		printf("当前延时%d\n",i);
		sleep(1);
	}
	printf("父亲第二次准备打钱了.....\n");
	pthread_mutex_lock(&mutex);//上锁
	money +=1000;//打钱
	pthread_mutex_unlock(&mutex);//解锁
	//父亲拿着喇叭 全部唤醒 所有在条件变量中等待的孩子
	pthread_cond_broadcast(&cond);
	
	pthread_join(thead1,NULL);
	pthread_join(thead2,NULL);
	pthread_join(thead3,NULL);
	pthread_join(thead4,NULL);
	
	
	//销毁互斥锁
	pthread_mutex_destroy(&mutex);
	//销毁条件变量 
	pthread_cond_destroy(&cond);
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值