导图社区 线程
这是一篇关于day4的思维导图,主要内容包括:设置线程属性,线程的同步,线程的互斥,线程。线程概念:为进一步减少处理器的空转时间,支持多处理器以及减少上下文切换开销,进程演化出线程,它是进程内独立的一条运行路线,是内核调度的最小单元。
编辑于2024-12-02 19:42:18这是一篇关于day5day6的思维导图,主要内容包括:消息队列:信息是带有类型的 可以过滤,IPC通信:共享内存 消息队列 信号灯集、ipcs命令 查看共享内存 消息队列 信号量、ipcs -s 查看信号量、ipcs -m 查看共享内存、ipcs -q 查看消息队列,共享内存,管道:在内核开辟的一块缓冲区,信号,进程间通信的方式。
这是一篇关于day4的思维导图,主要内容包括:设置线程属性,线程的同步,线程的互斥,线程。线程概念:为进一步减少处理器的空转时间,支持多处理器以及减少上下文切换开销,进程演化出线程,它是进程内独立的一条运行路线,是内核调度的最小单元。
这是一篇关于day3的思维导图,主要内容包括:守护进程,回收进程资源,孤儿进程和僵尸进程,exec族函数,结束进程,进程,Linux下的多任务机制。
社区模板帮助中心,点此进入>>
这是一篇关于day5day6的思维导图,主要内容包括:消息队列:信息是带有类型的 可以过滤,IPC通信:共享内存 消息队列 信号灯集、ipcs命令 查看共享内存 消息队列 信号量、ipcs -s 查看信号量、ipcs -m 查看共享内存、ipcs -q 查看消息队列,共享内存,管道:在内核开辟的一块缓冲区,信号,进程间通信的方式。
这是一篇关于day4的思维导图,主要内容包括:设置线程属性,线程的同步,线程的互斥,线程。线程概念:为进一步减少处理器的空转时间,支持多处理器以及减少上下文切换开销,进程演化出线程,它是进程内独立的一条运行路线,是内核调度的最小单元。
这是一篇关于day3的思维导图,主要内容包括:守护进程,回收进程资源,孤儿进程和僵尸进程,exec族函数,结束进程,进程,Linux下的多任务机制。
day4
线程
线程概念:为进一步减少处理器的空转时间,支持多处理器以及减少上下文切换开销,进程演化出线程,它是进程内独立的一条运行路线,是内核调度的最小单元。 (1)线程是轻量级进程(因为进程的效率低 才引入线程)//thread 线程 (2)线程依附于进程(1个进程可以创建多个线程,但是1个线程只能属于一个进程,如果进程结束,那么它所创建的线程也随之结束)//皮之不存 毛将焉附 (3)多线程只开辟栈区,所以线程的效率高

如何创建线程:pthread_t函数 功能:创建一个线程 返回值: 成功:0 失败:-1 //一个函数给调用者返回值一个值几种方式:2种 //返回值 参数上地址传递的方式
(2)头文件和函数原型 #include <pthread.h> //回调函数:参数中带有函数指针的函数 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); (3)参数说明: pthread_t *thread //通过参数上地址传递的方式,获取线程的ID const pthread_attr_t *attr //设置线程的属性 ,通常为NULL void *(*start_routine) (void *) //函数指针, 函数指针指向的函数,就是线程执行的功能函数 //指针的类型 挡住p void *(*) (void *) //指针指向的类型 挡住*p void *( ) (void *) void *arg //第四个参数是为第三个参数服务,给三个参数传参用的(给线程函数传递参数用的 //调用参考 void *fun(void *p) //线程执行的函数,线程 { 线程执行的代码; } pthread_t id; //用来保存线程的ID //通过参数上地址传递的方式,来获取线程的ID pthread_create(&id,NULL,fun,NULL);
编译的注意事项: gcc 01-创建线程.c -lpthread//在编译时 加载第三方动态库 加载第三方动态库 libpthread.so //linux中 动态库 以.so为结尾 静态库 以.a为结尾
循环创建多个线程
链接方式
静态链接:在编译的时候找函数,将函数的源代码从静态库原封不动的复制到源文件
动态链接:运行的时候找函数,编译的时候将函数地址从动态库复制到源文件中
静态链接和动态链接的区别: 1、可执行文件大小的角度:静态链接大 2、占用内存的角度:静态链接大 3、从效率角度来说:动态链接高 4、从升级角度:动态连接好 5、安装角度:静态链接好 (动态链接 多个链接文件如果误删一个 则无法链接成功)
静态库和动态库:
静态库:以.a为结尾,在编译的时候找函数,运行时不需要加载动态库,占用体积大
懂态库:以.so结尾,在运行的时候找函数,编译时将函数地址编译进源文件,运行时需要加载动态库,占用体积大
Windows和Linux都时动态链接
如何阻塞等待一个线程的结束
(2) 头文件及函数原型 #include <pthread.h> int pthread_join(pthread_t thread, void **retval); //调用参考 pthread_join(2345, NULL);//阻塞等待线程ID是2345这个线程结束,如果2345线程不结束,pthread_join一直阻塞等待,直到2345这个线程结束,pthread_join函数才会解除阻塞 (3)参数说明: pthread_t thread //阻塞等待线程结束的id void **retval //用来保存线程结束时的返回值,通常赋值为NULL
功能:用来阻塞等待指定线程的结束
返回值: 成功:0 失败:-1
给线程传递参数
无类型指针:void *p 无类型指针可以被任一类型指针赋值 但是 *p p++ ++p p+1 这些操作无法执行 将无类型指针强转成有类型指针
int num; void *p = # printf("%d\n",(*(int *)p);//方式一 int *q = p; printf("%d\n",*q);//方式二
传递整形:
#include"my.h" void* fun1(void *p)//p = &num { //方式一 将无类型指针赋值给有类型指针 int *q = p; printf("num is %d\n",*q); //方式二 将无类型指针强转使用 printf("num is %d\n",(*(int *)p)); } int main(int argc, const char *argv[]) { int num = 10; pthread_t id1; //传递整型 pthread_create(&id1,NULL,fun1,&num); pthread_join(id1,NULL); return 0; }
传递字符串:
#include"my.h" void* fun1(void *p)//p = &num { //方式一 将无类型指针赋值给有类型指针 int *q = p; printf("num is %d\n",*q); //方式二 将无类型指针强转使用 printf("num is %d\n",(*(int *)p)); } void* fun2(void *p)//p = s { //方式一 将无类型指针赋值给有类型指针 char *q = p; printf("s is %s\n",q); //方式二 将无类型指针强转使用 printf("s is %s\n",(char *)p); } int main(int argc, const char *argv[]) { int num = 10; char s[] = "hello world"; pthread_t id1,id2; //传递整型 pthread_create(&id1,NULL,fun1,&num); //传递字符串 pthread_create(&id2,NULL,fun2,s); pthread_join(id1,NULL); pthread_join(id2,NULL); return 0; }
传递结构体:
#include"my.h" typedef struct { int age; char name[20]; }stu_t; void* fun1(void *p)//p = &num { //方式一 将无类型指针赋值给有类型指针 int *q = p; printf("num is %d\n",*q); //方式二 将无类型指针强转使用 printf("num is %d\n",(*(int *)p)); } void* fun2(void *p)//p = s { //方式一 将无类型指针赋值给有类型指针 char *q = p; printf("s is %s\n",q); //方式二 将无类型指针强转使用 printf("s is %s\n",(char *)p); } void* fun3(void *p)//p = &a { //方式一 将无类型指针赋值给有类型指针 stu_t *q = p; printf("age is %d,name is %s\n",q->age,(*q).name); //方式二 将无类型指针强转使用 printf("age is %d,name is %s\n",((stu_t*)p)->age,(*(stu_t*)p).name); } int main(int argc, const char *argv[]) { stu_t a = {15,"xiaoming"}; /* s.age = 15; strcpy(s.name,"xiaoming"); */ int num = 10; char s[] = "hello world"; pthread_t id1,id2,id3; //传递整型 pthread_create(&id1,NULL,fun1,&num); //传递字符串 pthread_create(&id2,NULL,fun2,s); //传递结构体 pthread_create(&id3,NULL,fun3,&a); pthread_join(id1,NULL); pthread_join(id2,NULL); pthread_join(id3,NULL); return 0; }
线程的互斥
什么是互斥:多道程序环境下,多个进程访问或者操作同一个临界资源而造成争夺
如何解决互斥问题:
使用互斥锁解决互斥问题
互斥锁作用原理:线程访问任意一个共享资源时,必须先加锁,加锁成功才可以访问,加锁失败不可以访问,访问结束以后必须把锁解开
互斥锁使用方法
创建互斥锁
(1)头文件及函数原型 #include <pthread.h> int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *restrict attr); //调用参考 //mutex 互斥 pthread_mutex_t mutex = { 0 }; //定义一个结构体变量mutex pthread_mutex_init(&mutex,NULL); //初始化互斥锁,就是结构变量初始化 (2)参数说明: pthread_mutex_t *mutex //互斥锁 const pthread_mutexattr_t *restrict attr //互斥锁的属性,通常NULL
pthread_mutex_init
返回值: 成功:0 失败:返回错误编号
加锁
(1)头文件及函数原型 #include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex); //调用参考 //lock 锁 pthread_mutex_lock(&mutex); (2)参数说明: pthread_mutex_t *mutex //互斥锁
pthread_mutex_lock
返回值: 成功:0 失败:返回错误编号
解锁
(1)头文件及函数原型 #include <pthread.h> int pthread_mutex_unlock(pthread_mutex_t *mutex); //调用参考 pthread_mutex_unlock(&mutex); (2)参数说明: pthread_mutex_t *mutex //互斥锁
pthread_mutex_unlock
返回值: 成功:0 失败:返回错误编号
使用信号量解决互斥问题
设置线程属性
测试线程堆栈大小 默认情况堆栈大小(有些系统1M, 有些2M, 有些4M, 有些8M) 如果定义一个局部变量占用空间特别大,要改堆栈大小
#include"my.h" void* fun(void *p) { //1G = 1024M //1M = 1024kb //1kb = 1024byte char a[8*1024*1024];//8M 栈空间不足 导致程序崩溃 while(1) { printf("11111111\n"); sleep(1); } } int main(int argc, const char *argv[]) { pthread_t id; pthread_create(&id,NULL,fun,NULL); pthread_join(id,NULL); return 0; }
修改线程堆栈大小
pthread_attr_init函数
(2)头文件及函数原型 #include <pthread.h> int pthread_attr_init(pthread_attr_t *attr); (3)参数 pthread_attr_t *attr //通过参数上地址传递的方式获取线程的属性 //调用参考 pthread_attr_t attr = { 0 };//用来存储默认的线程属性 pthread_attr_init(&attr);//通过参数上地址传递的方式获取线程的属性,保存到attr变量中
功能:获取当前线程默认的属性
返回值: 成功:0 失败:非零的错误编号
pthread_attr_setstacksize函数
(2)头文件及函数原型 #include <pthread.h> int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); (3)参数 pthread_attr_t *attr //线程的属性 size_t stacksize //线程属性中的堆栈大小被设置的新的大小值 以字节为单位 //调用参考 pthread_attr_setstacksize(&attr, 10*1024*1024);//设置线程属性中的堆栈大小为10M
功能:设置当前线程的堆栈大小
返回值: 成功:0 失败:非零的错误编号 time_t t; time(&t); ctime(&t);
线程的同步
什么是同步:在异步环境下一组并发进程 相互制约 相互等待 相互配合按照一定顺序执行
如何解决线程的同步
使用信号量解决多线程之间的同步问题
使用互斥锁+条件唤醒可以解决多线程之间的同步
信号量是什么:可以将信号量理解成一类资源 将一个信号量理解成停车场 信号量的个数就是 停车场中停车位的个数
信号量作用:解决多线程之间互斥和同步问题
信号量使用方法
sem_init:创建信号量:初始化一个停车场
(2) 头文件及函数原型 #include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value); (3)参数说明: sem_t *sem //信号量(停车场) int pshared //通常为0,表示信号量在线程之间共享 unsigned int value //停车位的个数 //调用参考 sem_t sem = { 0 }; //定义一个结构体变量名字叫sem, 创建一个停车场 //第二个参数0,代表多个线程之间共享这个信号量 sem_init(&sem, 0, 0); //停车位数为0, 用来解决多线程的同步问题 sem_init(&sem, 0, 1); //停车位数为1, 类似于于互斥锁,用来解决多线程的互斥问题
功能:对信号量进行初始化
返回值: 成功:0 失败:-1
sem_wait:请求信号量:申请一个停车位,停车位个数-1 没有停车位 停车失败 阻塞等待
2)头文件及函数原型 #include <semaphore.h> int sem_wait(sem_t *sem); (3)参数说明: sem_t *sem //一但请求信号量成功,停车位-1 ,请求失败,阻塞等待 //调用参考 //何时会出现请求失败??? 当停车场停车位为0的时候,请求失败 sem_wait(&sem);//信号量,一但请求信号成功,停车位-1 ,请求失败,阻塞等待
功能:请求信号量
返回值: 成功:0 失败:-1
sem_post:释放信号量:释放一个停车位 停车位个数+1 不是车开走了!
(2)头文件及函数原型 #include <semaphore.h> int sem_post(sem_t *sem); (3)参数说明: sem_t *sem //释放信号量,停车位+1 //调用参考 sem_post(&sem);
功能:释放信号量
返回值: 成功:0 失败:-1
信号量解决互斥问题

信号量解决同步问题

多线程共享信号量
sem_init(&sem, 0, 3); 线程A 调用sem_wait(&sem); //请求信号量,因为个数为3,请求成功,停车位 -1 ---> 2 线程B 调用sem_wait(&sem); //请求信号量,因为个数为2,请求成功,停车位 -1 ---> 1 线程C 调用sem_wait(&sem); //请求信号量,因为个数为1,请求成功,停车位 -1 ---> 0 线程D 调用sem_wait(&sem); //请求信号量,因为个数为0,请求失败,线程D处于阻塞等待状态, 如果线程A 调用sem_post(&sem); //线程A释放一个信号量 停车位 +1 ,线程D可以请求成功,停车位-1 ---> 0