目录
4.exec函数簇----system("tftp -g -r 1.txt 192.168.10.18")
6.进程之间的IPC(inter process communicate)重点
1)全局变量2)通过传参数的方式3)通过pthread_exit的退出值
2)有名信号量 -> 进程 (共享内存+有名信号量一起使用) <编译的时候需要链接线程库-lpthread>
3)条件变量 -> 线程(互斥锁+条件变量一起使用) ---->重点
fork()
exit()
wait() //获取子进程退出的状态值
如果父进程想要接受子进程的退出的状态值,子进程就需要使用exit()函数
- int main(int argc,char **argv)
- {
- pid_t id;
-
- id = fork();
- if(id == -1)
- {
- //perror("fork fail");
- printf("fork fail\n");
- return -1;
- }
- else if(id > 0)//父进程
- {
- sleep(2);
- printf("[%d]我是你爹 正在打牌 儿子的ID号:%d\n",getpid(),id);
-
- wait(NULL);//阻塞等待子进程的退出,并回收它的资源
- }
- else if(id == 0)//子进程
- {
- printf("[%d]我是你的好大儿 出去浪 我爹的id号:%d\n",getpid(),getppid());
- }
-
-
- return 0;
- }
孤儿进程:父进程先退出,子进程还在;孤儿进程的资源它是由系统来回收。
僵尸进程:父进程还在一直在干自己的事,子进程先退出;父进程没有回收它的资源。
fork父进程与子进程 拥有独立的内存空间,互不影响。
vfork子进程与父进程共享内存空间,更加准确来说,子进程在调用exec函数或者exit函数之前内存空间是共享的
- int main()
- {
- //原本进程的代码
- int a = 100; //a是局部变量,是栈区
- //static int a = 100; //a是静态变量,存放在数据段
- printf("main\n");
-
- //使用vfork创建一个子进程,父进程与子进程共享数据段
- pid_t id = vfork();
- if(id == -1)//出错
- {
- printf("fork error\n");
- return -1;
- }
- else if(id >0)//父进程
- {
- printf("parent:%d a addr:%p value:%d\n",getpid(),&a,a);
- }
- else if(id == 0)//子进程
- {
- a = 250; //子进程修改变量a的值
- printf("child:%d a addr:%p value:%d\n",getpid(),&a,a);
- sleep(5);
- exit(0);//让子进程到这里就结束
- }
-
- printf("111\n");
- //阻塞等待 子进程退出
- wait(NULL);
-
- return 0;
- }
第一种方法:
execl("./a.out", "a.out", "abcd", NULL); 相当于跑了一条指令./a.out abcd
第二种方法:
char *const argv[ ] = {"hello","1.c","2.c",NULL};
execv("./hello", argv);
说明:
l->列表 v->argv p->指定路径 e->自定义路径
fd[1]为写入端(入队),fd[0]为读出端(出队)
pipe()
- int main(int argc, char * argv [ ])
- {
- //创建一个数组
- int fd[2]; //fd[0]读 fd[1]写
-
- //创建管道
- pipe(fd);
- printf("fd[0]:%d fd[1]:%d\n",fd[0],fd[1]);
-
- pid_t id;
- id = fork();
- if(id > 0)
- {
- //父进程从管道给老大儿打钱
- write(fd[1],"money 100",strlen("money 100"));
- //wait(NULL);
- }
- else if(id == 0)
- {
- //老大儿从管道拿钱
- //sleep(1);
- char buf[1024] = {0};
- int ret = read(fd[0],buf,1024);
- printf("buf:%s ret=%d\n",buf,ret);
- }
-
- wait(NULL);//父进程阻塞等待子进程退出
-
- return 0;
- }
mkfifo()
管道特点:
对于读数据来说,管道里面有什么就拿什么
无名管道和有名管道都是通过系统IO函数来操作 open read write
write
- #define FIFO_FILE "/home/gec/fifo2" //有名管道
-
- int main(int argc, char * argv [ ])
- {
- int ret;
- //先判断文件是否存在,如果存在则不用创建了
- if(access(FIFO_FILE,F_OK) == -1)//access 判断文件是否存在,如果不存在则返回 -1
- {
- ret = mkfifo(FIFO_FILE,0777);
- if(ret == -1)
- {
- perror("mkfifo fail");
- return -1;
- }
- }
-
- //打开管道
- int fd;
- fd = open(FIFO_FILE,O_RDWR);
- if(fd < 0)
- {
- perror("open fifo fail");
- return -1;
- }
-
- while(1)
- {
- sleep(1);
- write(fd,"money 250",strlen("money 250"));
- }
-
- return 0;
- }
read
- #define FIFO_FILE "/home/gec/fifo2" //有名管道
-
- int main(int argc, char * argv [ ])
- {
- int ret;
- //先判断文件是否存在,如果存在则不用创建了
- if(access(FIFO_FILE,F_OK) == -1)//access 判断文件是否存在,如果不存在则返回 -1
- {
- ret = mkfifo(FIFO_FILE,0777);
- if(ret == -1)
- {
- perror("mkfifo fail");
- return -1;
- }
- }
-
- //打开有名管道
- int fd;
- fd = open(FIFO_FILE,O_RDWR);
- if(fd < 0)
- {
- perror("open fifo fail");
- return -1;
- }
-
- char buf_recv[1024] = {0};
- while(1)
- {
- memset(buf_recv,0,sizeof(buf_recv));
- ret = read(fd,buf_recv,sizeof(buf_recv));
- printf("buf_recv:%s,ret:%d\n",buf_recv,ret);
- }
-
- return 0;
- }
对于消息队列来说,我可以通过数据的编号,来拿取对应的数据
ftok(路径)、msgget(文件描述符)、msgsnd、msgrcv
练习1:
将无名、有名、消息队列的代码跑一遍
msg_snd
- //进程1 : 往消息队列中发送数据
-
- //自己定义的消息队列的数据结构体
- struct msgbuf{
- long mtype;//数据的编号/类型
- char mtext[1024]; //数据的正文
- };
-
- int main()
- {
- //1、确定 文件路径名 ---获取消息队列的key值
- key_t key = ftok(".", 100);
-
- //2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建
- int msgid = msgget(key,IPC_CREAT|0666);
- if(msgid == -1){
- perror("msgget error");
- return -1;
- }
-
- printf("消息队列 key:%#x msgid:%d\n",key,msgid); //0x
-
- //3、往文件中写入数据 --------往消息队列中 发送数据
- struct msgbuf data;
- memset(&data,0,sizeof(data));
-
- strcpy(data.mtext,"nihao");//字符数组拷贝
- data.mtype = 10;
-
- struct msgbuf data2;
- memset(&data2,0,sizeof(data2));
-
- strcpy(data2.mtext,"nihao2");//字符数组拷贝
- data2.mtype = 20;
-
- while(1)
- {
- int ret = msgsnd(msgid, &data,strlen(data.mtext),0); //发送数据报文1
- if(ret == -1)
- {
- perror("msgsnd error");
- return -1;
- }
- msgsnd(msgid, &data2,strlen(data2.mtext),0); //发送数据报文2
- }
-
- return 0;
- }
msg_recv
- //进程2 : 从消息队列中 获取数据
-
- //自己定义的消息队列的数据结构体
- struct msgbuf{
- long mtype;//数据的编号/类型
- char mtext[1024]; //数据的正文
- };
-
-
- int main()
- {
- //1、确定 文件路径名 ---获取消息队列的key值
- key_t key = ftok(".", 100);
-
- //2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建
- int msgid = msgget(key,IPC_CREAT|0666);
- if(msgid == -1)
- {
- perror("msgget error");
- return -1;
- }
-
- printf("消息队列 key:%#x msgid:%d\n",key,msgid); //0x
-
- //3、往文件中写入数据 --------往消息队列中 发送数据
- struct msgbuf data;
- memset(&data,0,sizeof(data));
- while(1)
- {
- int ret = msgrcv(msgid, &data, sizeof(data.mtext),20,0); //只接受数据报文2
- printf("msgrcv ret :%d data:%s\n",ret,data.mtext);
- sleep(1);
- }
-
- //4、最后不需要 使用到这条消息队列的时候,删除即可
- msgctl(msgid, IPC_RMID, NULL);
-
- return 0;
- }
ftok、shmget、shmat(地址映射)、shmdt(解除)、shmctl(删除)、直接在这个地址里面进行内存拷贝
shm_wr
- //进程1--写端
- int main()
- {
- //1、申请key值
- key_t key = ftok(".",10);
-
- //2、根据key值, 得到物理共享内存的ID号,如果该物理内存不存在,则创建 ---open
- int shmid = shmget(key,1024,IPC_CREAT|0666);
- printf("共享内存 key:%#x shmid:%d\n",key,shmid);
-
- //3、将物理内存映射到用户的虚拟内存空间中的某一块区域(类似于mmap的功能)
- char*shm_p = shmat(shmid,NULL,0);
- if(shm_p == (void*)-1)
- {
- perror("shmat error");
- return -1;
- }
-
- //4、直接往这块内存进行赋值(写入数据)
- //可以不断从键盘上获取数据,当输入exit的时候退出
- char sendbuf[1024] = "hello world";
- memcpy(shm_p,sendbuf, strlen(sendbuf)); //内存拷贝的操作
-
- while(1);
-
- //5、最后不用的时候解除 映射
- shmdt(shm_p);
-
- return 0;
- }
shm_rd
- //进程2
-
- int main()
- {
- //1、申请key值
- key_t key = ftok(".",10);
-
- //2、根据key值, 得到物理共享内存的ID号,如果该物理内存不存在,则创建 ---open
- int shmid = shmget(key,1024,IPC_CREAT|0666);
- printf("共享内存 key:%#x shmid:%d\n",key,shmid);
-
- //3、将物理内存映射到用户的虚拟内存空间中的某一块区域
- char*shm_p = shmat(shmid,NULL,0);
- if(shm_p == (void*)-1)
- {
- perror("shmat error");
- return -1;
- }
-
- while(1)
- {
- //4、直接往这块内存读取数据
- //不断地读取数据,当获取到exit的时候退出
- printf("%s\n",shm_p);//从共享内存里面读数据
- }
-
- while(1);
-
- //4、最后不用的时候解除 映射
- shmdt(shm_p);
-
- //5、删除共享内存
- shmctl(shmid,IPC_RMID,NULL);
-
- return 0;
- }
>它不能单独使用,是和共享内存一起使用
>操作流程如下
初始化共享内存
初始化信号量
进程一:写
空间P操作
写数据
数据V操作
进程二:读
数据P操作
读数据
空间V操作
练习2:
跑一下信号量的代码,理解它的流程
shm_sem_wr
- int main()
- {
- //1、获取key值
- key_t key = ftok(".",10);
- //2、根据key值 获取共享内存的ID号
- int shmid = shmget(key,1024,IPC_CREAT|0666);
- //3、根据ID号 将共享内存映射至本进程虚拟内存空间的某个区域
- char*shm_p = shmat(shmid,NULL,0);
- if(shm_p == (void*)-1)
- {
- perror("shmat error");
- return -1;
- }
-
- //4、获取信号量的key值
- key_t key1 = ftok(".",20);
- //5、根据key值申请信号量ID号
- int semid = semget(key1,2,IPC_CREAT|0666);
- //6、初始化信号量起始值 (semnum:需要操作的成员的下标 空间:0 数据:1)
- semctl(semid,0,SETVAL,1);//设置空间的起始值为1 //有空间
- semctl(semid,1,SETVAL,0);//设置数据的起始值为0 //无数据
-
-
- //空间结构体初始化
- struct sembuf space;
- space.sem_num = 0;//空间
- space.sem_op = -1;//P操作
- space.sem_flg = 0;//普通属性
-
- //数据结构体初始化
- struct sembuf data;
- data.sem_num = 1;//数据
- data.sem_op = 1;//V操作
- data.sem_flg = 0;//普通属性
-
- //此时映射出来的shm_p 就是两个进程的共享内存
- while(1)
- {
- //空间:1 数据:0
- //开车进去之前,空间 -1 --P操作
- semop(semid, &space, 1);//请问空间能不能P操作?
-
- //能 -> 有车位 -> 函数返回
- //不能 -> 没车位 -> 函数阻塞
-
-
- //开车进去
- //从键盘上获取数据,存储到共享内存shm_p
- scanf("%s",shm_p);
-
- //开车进去之后,数据+1 --V操作
- semop(semid, &data, 1); //数据自动+1
-
- //退出条件,这里要注意 应该使用strncmp 指定字节数
- if(strncmp(shm_p,"exit",4) == 0)
- break;
- }
-
- //4、当不再使用时,解除映射关系
- shmdt(shm_p);
- //5、当没有进程再需要使用这块共享内存时,删除它
- shmctl(shmid, IPC_RMID, NULL);
-
- semctl(semid,0,IPC_RMID);
-
- return 0;
- }
shm_sem_rd
- int main()
- {
- //1、获取key值
- key_t key = ftok(".",10);
- //2、根据key值 获取共享内存的ID号
- int shmid = shmget(key,1024,IPC_CREAT|0666);
- //3、根据ID号 将共享内存映射至本进程虚拟内存空间的某个区域
- char*shm_p = shmat(shmid,NULL,0);
- if(shm_p == (void*)-1)
- {
- perror("shmat error");
- return -1;
- }
-
- //4、获取信号量的key值
- key_t key1 = ftok(".",20);
- //5、根据key值申请信号量ID号
- int semid = semget(key1,2,IPC_CREAT|0666);
- //6、初始化信号量起始值
- semctl(semid,0,SETVAL,1);//设置空间的起始值为1
- semctl(semid,1,SETVAL,0);//设置数据的起始值为0
-
- //空间结构体初始化
- struct sembuf space;
- space.sem_num = 0;
- space.sem_op = 1; //V操作
- space.sem_flg = 0;
-
- //数据结构体初始化
- struct sembuf data;
- data.sem_num = 1;
- data.sem_op = -1;//P操作
- data.sem_flg = 0;
-
- //此时映射出来的shm_p 就是两个进程的共享内存
- while(1)
- {
- //空间:0 数据:1
- //把车开出来之前,请问数据能不能-1?
- semop(semid, &data, 1);
-
- //能 -> 里面有车 -> 函数返回
- //不能 -> 里面没车 -> 函数阻塞
-
- //从车库里面把车开出来
- //从共享内存中读取数据
- printf("recv:%s\n",shm_p);
- //sleep(1);
-
- //把车开出来之后,空间+1
- semop(semid, &space, 1);
-
- //空间:1 数据:0
-
- //退出条件,这里要注意 应该使用strncmp 指定字节数
- if(strncmp(shm_p,"exit",4) == 0)
- break;
- }
-
- return 0;
- }
int kill(pid_t pid, int sig);
kill -9 进程id
killall -9 进程名
signal(函数句柄)
sighandler_t signal(int signum, sighandler_t handler);
int pause(void);
raise()
添加信号的阻塞属性
守护进程(Daemon)也被翻译为精灵进程、后台进程,是一种旨在运行于相对干净环境、不受终端影响的、
常驻内存的进程,就像神话中的精灵拥有不死的特性,长期稳定提供某种功能或服务。
pthread_create()--->函数指针(回调函数)
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
pthread_exit()
void pthread_exit(void *retval);
pthread_join()
int pthread_join(pthread_t thread, void **retval);
- int data = 300;
-
- void *fun(void *arg)
- {
- //通过传参拿到主线程给子线程传的值
- int num = *((int *)arg);
- printf("%d\n",*((int *)arg));
-
- //pthread_exit("hello");//"hello"是char *类型
- pthread_exit((void *)&data);
- }
-
- int main(int argc,char **argv)
- {
- int num = 250;
- //&num是int *类型;int *是void *类型中的一种
-
- int ret;
- pthread_t tid;
- ret = pthread_create(&tid,NULL,fun,&num);
- if(ret != 0)
- {
- printf("pthread_create fail\n");
- return -1;
- }
-
- void *retval;
- pthread_join(tid,&retval);
- //printf("retval = %s\n",(char *)retval);
- printf("retval = %d\n",*((int *)retval));
-
- return 0;
- }
练习3:通过传参的方式将一个学生的信息传给子线程
struct st
{
char name[20];
char sex;
int hight;
float score;
};
- struct st
- {
- char name[20];
- char sex;
- int hight;
- float score;
- };
-
- int data = 300;
-
- void *fun(void *arg)
- {
- //用结构体变量接收
- //struct st s = *(struct st *)arg;
- //printf("%-20s%-5c%-10d%-10.2f\n",s.name,s.sex,s.hight,s.score);
-
- //用结构体指针变量接收
- struct st *s = (struct st *)arg;
- printf("%-20s%-5c%-10d%-10.2f\n",s->name,s->sex,s->hight,s->score);
-
- pthread_exit((void *)&data);
- }
-
- int main(int argc,char **argv)
- {
-
- struct st s = {"bling",'M',188,98.5};
- //&s是struct st *类型;struct st *是void *类型中的一种
-
- int ret;
- pthread_t tid;
- ret = pthread_create(&tid,NULL,fun,&s);
- if(ret != 0)
- {
- printf("pthread_create fail\n");
- return -1;
- }
-
- void *retval;
- pthread_join(tid,&retval);
- //printf("retval = %s\n",(char *)retval);
- printf("retval = %d\n",*((int *)retval));
-
- return 0;
- }
pthread_attr_t attr;
int pthread_attr_init(pthread_attr_t *attr); //初始化
int pthread_attr_destroy(pthread_attr_t *attr); //销毁
pthread_create(&tid,&attr,.....);
pthread_detach(pthread_self());
pthread_cancel()
pthread_setcanselstate();//能不能
pthread_setcanseltype();//立即还是延迟取消
压栈线程的取消例程函数-> pthread_cleanup_push()
#include
void pthread_cleanup_push(void (*routine)(void *),void *arg);
弹栈线程的取消例程函数 --> pthread_cleanup_pop()
#include
void pthread_cleanup_pop(int execute);
空间+数据一起操作(很麻烦)
数据(比信号量要简单)
sem_wait() //P操作
sem_post() //V操作
sem1
- #define SEM_NAME "/semname1" //要以"/"开头
-
- int main()
- {
- //1、获取key值
- key_t key = ftok(".",10);
- //2、根据key值 获取共享内存的ID号
- int shmid = shmget(key,1024,IPC_CREAT|0666);
- //3、根据ID号 将共享内存映射至本进程虚拟内存空间的某个区域
- char*shm_p = shmat(shmid,NULL,0);
- if(shm_p == (void*)-1)
- {
- perror("shmat error");
- return -1;
- }
-
- //4、创建并打开一个有名信号量
- sem_t *nameSem = sem_open(SEM_NAME,O_CREAT,0777,0);
- if(nameSem == SEM_FAILED )
- {
- printf("sem_open error\n");
- return -1;
- }
-
- //此时映射出来的shm_p 就是两个进程的共享内存
- while(1)
- {
- //从键盘上获取数据,存储到共享内存shm_p
- scanf("%s",shm_p);
-
- //有名信号量的V操作 数据 +1
- sem_post(nameSem);
-
- //退出条件,这里要注意 应该使用strncmp 指定字节数
- if(strncmp(shm_p,"exit",4) == 0)
- break;
- }
-
- //4、当不再使用时,解除映射关系
- shmdt(shm_p);
- //5、当没有进程再需要使用这块共享内存时,删除它
- shmctl(shmid, IPC_RMID, NULL);
-
- //6、关闭有名信号量。
- sem_close(nameSem);
- //7、删除有名信号量
- sem_unlink(SEM_NAME);
-
- return 0;
- }
sem2
- #define SEM_NAME "/semname1"
-
- int main()
- {
- //1、获取key值
- key_t key = ftok(".",10);
- //2、根据key值 获取共享内存的ID号
- int shmid = shmget(key,1024,IPC_CREAT|0666);
- //3、根据ID号 将共享内存映射至本进程虚拟内存空间的某个区域
- char*shm_p = shmat(shmid,NULL,0);
- if(shm_p == (void*)-1)
- {
- perror("shmat error");
- return -1;
- }
-
- //4、创建并打开一个有名信号量
- sem_t *nameSem = sem_open(SEM_NAME,O_CREAT,0777,0);
- if(nameSem == SEM_FAILED )
- {
- printf("sem_open error\n");
- return -1;
- }
-
- //此时映射出来的shm_p 就是两个进程的共享内存
- while(1)
- {
- //有名信号量的P操作数据 -1
- sem_wait(nameSem);
-
- //从共享内存中读取数据
- printf("recv:%s\n",shm_p);
-
- //退出条件,这里要注意 应该使用strncmp 指定字节数
- if(strncmp(shm_p,"exit",4) == 0)
- break;
- }
-
- return 0;
- }
数据(比信号量要简单)
sem_wait() //P操作
sem_post() //V操作
- //定义一个无名信号量作用于整个进程
- sem_t g_sem;
-
- int g_val = 0;
-
- //线程例程,创建一条线程之后,去执行这个函数
- void* start_routine1(void *arg)
- {
- //数据P操作
- sem_wait(&g_sem);
-
- g_val = 100;
- sleep(2);//延时2秒,
-
- printf("start_routine1 g_val:%d\n",g_val);
-
- //当这个线程不再使用这个共享资源的时候,无名信号量 进行 V操作
- sem_post(&g_sem);
-
- pthread_exit(NULL);
- }
-
- //线程例程,创建一条线程之后,去执行这个函数
- void* start_routine2(void *arg)
- {
- //数据P操作
- sem_wait(&g_sem);
-
- sleep(1);//延时1秒
- g_val = 200;
-
- printf("start_routine2 g_val:%d\n",g_val);
-
- //当这个线程不再使用这个共享资源的时候,无名信号量 进行 V操作
- sem_post(&g_sem);
-
- pthread_exit(NULL);
- }
-
- //线程例程,创建一条线程之后,去执行这个函数
- void* start_routine3(void *arg)
- {
- //数据P操作
- sem_wait(&g_sem);
-
- //sleep(1);//延时1秒
- g_val = 250;
-
- printf("start_routine3 g_val:%d\n",g_val);
-
- //当这个线程不再使用这个共享资源的时候,无名信号量 进行 V操作
- sem_post(&g_sem);
-
- pthread_exit(NULL);
- }
-
- int main()
- {
- int ret;
-
- //无名信号量的初始化
- sem_init(&g_sem,0,1);
-
- //创建一条子线程
- pthread_t thread1;
- ret = pthread_create(&thread1,NULL,start_routine1,NULL);
- if(ret != 0)
- {
- printf("pthread_create fail\n");
- return -1;
- }
-
- //创建一条子线程
- pthread_t thread2;
- ret = pthread_create(&thread2,NULL,start_routine2,NULL);
- if(ret != 0)
- {
- printf("pthread_create fail\n");
- return -1;
- }
-
- //创建一条子线程
- pthread_t thread3;
- ret = pthread_create(&thread3,NULL,start_routine3,NULL);
- if(ret != 0)
- {
- printf("pthread_create fail\n");
- return -1;
- }
-
-
- //数据P操作
- sem_wait(&g_sem);
- g_val = 1000;
- printf("main g_val:%d\n",g_val);
-
- //当这个线程不再使用这个共享资源的时候,无名信号量 进行 V操作
- sem_post(&g_sem);
-
- //等待子线程退出
- pthread_join(thread1,NULL);
- pthread_join(thread2,NULL);
- pthread_join(thread3,NULL);
-
- //销毁无名信号量
- sem_destroy(&g_sem);
-
- return 0;
- }
动态初始化
pthread_mutex_t m;
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
静态初始化
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
上锁与解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);//互斥锁上锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);//销毁锁
- //全局变量,用于多线程之间共同访问
- int g_val = 10;
-
- //方法一:1.定义一个互斥锁变量
- pthread_mutex_t mutex;
-
- //方法二:静态初始化一个互斥锁
- //pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-
- void *fun1(void *arg)
- {
- //设置子线程自己为分离属性不需要主线程join
- pthread_detach(pthread_self());
-
- //上锁(传的都是锁的地址)
- pthread_mutex_lock(&mutex);
- g_val = 20;
- printf("[%s]g_val = %d\n",__FUNCTION__,g_val);
-
- //解锁
- pthread_mutex_unlock(&mutex);
- }
-
- void *fun2(void *arg)
- {
- //设置子线程自己为分离属性不需要主线程join
- pthread_detach(pthread_self());
-
- //上锁
- pthread_mutex_lock(&mutex);
- g_val = 200;
- printf("[%s]g_val = %d\n",__FUNCTION__,g_val);
-
- //解锁
- pthread_mutex_unlock(&mutex);
- }
-
-
- int main(int argc,char **argv)
- {
-
- //2.初始化互斥锁(注意传的是锁的地址)
- //pthread_mutex_init(&mutex,NULL);
-
- int ret;
- pthread_t tid1,tid2;
- //创建线程fun1
- ret = pthread_create(&tid1,NULL,fun1,NULL);
- if(ret != 0)
- {
- printf("pthread_create fail\n");
- return -1;
- }
-
- //创建线程fun2
- ret = pthread_create(&tid2,NULL,fun2,NULL);
- if(ret != 0)
- {
- printf("pthread_create fail\n");
- return -1;
- }
-
- //上锁
- pthread_mutex_lock(&mutex);
- g_val = 250;
- printf("[%s]g_val = %d\n",__FUNCTION__,g_val);
- //解锁
- pthread_mutex_unlock(&mutex);
-
- sleep(2);
-
- //销毁锁
- pthread_mutex_destroy(&mutex);
-
- return 0;
- }
读锁就是一把共享锁
写锁就是一把互斥锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//读锁上锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//写锁上锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);//读写解锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);//销毁锁
- //全局变量,用于多线程之间共同访问
- int g_val = 10;
-
- //1.定义一个读写锁变量
- pthread_rwlock_t rwlock;
-
-
- void *fun1(void *arg) //写操作
- {
- //上写锁
- pthread_rwlock_wrlock(&rwlock);
- int cnt=3;
- while(cnt--)
- {
- g_val = 20;
- printf("[%s]g_val = %d\n",__FUNCTION__,g_val);
- }
- //解锁
- pthread_rwlock_unlock(&rwlock);
-
- }
-
- void *fun2(void *arg)//写操作
- {
- //上写锁
- pthread_rwlock_wrlock(&rwlock);
- int cnt=3;
- while(cnt--)
- {
- g_val = 200;
- printf("[%s]g_val = %d\n",__FUNCTION__,g_val);
- }
- //解锁
- pthread_rwlock_unlock(&rwlock);
- }
-
- void *fun3(void *arg)//读操作
- {
- //上读锁
- pthread_rwlock_rdlock(&rwlock);
- int cnt=3;
- while(cnt--)
- {
- printf("[%s]g_val = %d\n",__FUNCTION__,g_val);
- sleep(1);
- }
- //解锁
- pthread_rwlock_unlock(&rwlock);
- }
-
- void *fun4(void *arg)//读操作
- {
-
- //上读锁
- pthread_rwlock_rdlock(&rwlock);
- int cnt=3;
- while(cnt--)
- {
- printf("[%s]g_val = %d\n",__FUNCTION__,g_val);
- sleep(1);
- }
- //解锁
- pthread_rwlock_unlock(&rwlock);
- }
-
-
- int main(int argc,char **argv)
- {
-
- //2.读写锁的初始化
- pthread_rwlock_init(&rwlock,NULL);
-
- int ret;
- pthread_t tid1,tid2,tid3,tid4;
- //创建线程fun1
- ret = pthread_create(&tid1,NULL,fun1,NULL);
- if(ret != 0)
- {
- printf("pthread_create fail\n");
- return -1;
- }
-
- //创建线程fun2
- ret = pthread_create(&tid2,NULL,fun2,NULL);
- if(ret != 0)
- {
- printf("pthread_create fail\n");
- return -1;
- }
-
- //创建线程fun3
- ret = pthread_create(&tid3,NULL,fun3,NULL);
- if(ret != 0)
- {
- printf("pthread_create fail\n");
- return -1;
- }
-
- //创建线程fun4
- ret = pthread_create(&tid4,NULL,fun4,NULL);
- if(ret != 0)
- {
- printf("pthread_create fail\n");
- return -1;
- }
-
- //上写锁
- pthread_rwlock_wrlock(&rwlock);
- g_val = 250;
- printf("[%s]g_val = %d\n",__FUNCTION__,g_val);
- //解锁
- pthread_rwlock_unlock(&rwlock);
-
- pthread_join(tid1,NULL);
- pthread_join(tid2,NULL);
- pthread_join(tid3,NULL);
- pthread_join(tid4,NULL);
-
- //销毁读写锁
- pthread_rwlock_destroy(&rwlock);
-
- return 0;
- }
//初始化互斥锁
//初始化条件变量
//自动解锁 ,并且阻塞等待
pthread_cond_wait(&cond,&mutex);
- //全局变量-临界资源(银行卡余额)
- int g_money = 2000;
-
- //定义一个互斥锁变量
- pthread_mutex_t mutex;
-
- //定义一个条件变量并且静态初始化
- pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
-
- void *start_routine(void*arg)
- {
- printf("[%lu]子线程 start\n",pthread_self());
-
- pthread_mutex_lock(&mutex);//加锁 力度要小
-
- //条件不满足的时候进入 条件变量中等待
- while(g_money<1000)
- {
- printf("没钱了,进去条件变量中等待父母打钱 并且通知.....\n");
-
- //自动解锁 ,并且阻塞等待
- pthread_cond_wait(&cond,&mutex);
- printf("父母打钱过来了,已经通知我了,此时余额:%d\n",g_money);
- }
-
- //走到这里,说明有钱
- g_money -=1000;
- printf("[%lu]子线程 拿到钱了,此时银行卡余额:%d\n",pthread_self(),g_money);
-
- pthread_mutex_unlock(&mutex); //解锁
-
- printf("[%lu]子线程 end\n",pthread_self());
-
- //拿钱走人
- pthread_exit(NULL);
- }
-
- int main()
- {
- //初始化互斥锁
- pthread_mutex_init(&mutex,NULL);
-
- pthread_t thread1; //我
- pthread_create(&thread1,NULL,start_routine, NULL);
- pthread_t thread2; //哥
- pthread_create(&thread2,NULL,start_routine, NULL);
- pthread_t thread3; //姐
- pthread_create(&thread3,NULL,start_routine, NULL);
- pthread_t thread4; //弟
- pthread_create(&thread4,NULL,start_routine, NULL);
-
-
- int cnt=5;
- while(cnt--)
- {
- sleep(1);
- printf("主线程(父母) 即将准备打钱....%d\n",cnt);
- }
-
- //主线程(父母) 打钱
- pthread_mutex_lock(&mutex);//加锁
- g_money +=1000;
- pthread_mutex_unlock(&mutex); //解锁
- pthread_cond_broadcast(&cond);//广播: 唤醒所有在条件变量中等待的线程
-
- cnt=5;
- while(cnt--)
- {
- sleep(1);
- printf("主线程(父母) 即将准备打钱....%d\n",cnt);
- }
- pthread_mutex_lock(&mutex);//加锁
- g_money +=1000;
- pthread_mutex_unlock(&mutex); //解锁
- pthread_cond_signal(&cond);//单播: 随机唤醒一个在条件变量中等待的线程
-
- pthread_join(thread1, NULL);
- pthread_join(thread2, NULL);
- pthread_join(thread3, NULL);
- pthread_join(thread4, NULL);
-
- pthread_mutex_destroy(&mutex);
- pthread_cond_destroy(&cond);
-
- return 0;
- }