一、验证当前一个进程中最多只能开多少条线程?
#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;
}