文章目录
- 3.1线程概述
- 线程概述
- 线程和进程区别
- 线程和进程虚拟地址空间
- 线程之间共享和非共享资源
- NPTL
- 3.2 创建线程
- 线程操作
- 创建线程
- 出现报错及原因
- 3.3终止线程
- 3.4连接已终止的线程
3.1线程概述
线程概述
并发:两队人用同一个咖啡机(本质上同一时刻只有一个进程在运行,但是切换非常快,所以看起来是好几个进程同时进行)
并行:两队人用两个咖啡机
查看指定进程的线程:ps -Lf pid(进程号)
线程和进程区别
线程和进程虚拟地址空间
进程是读时共享,写时复制。但不论怎么说,只要创建了一个新进程,内核一定先被复制。写的时候还要创建新的实际物理空间。
线程共享虚拟地址空间,只是**.text段(代码段)以及栈空间**有不一样。.text会分成一个个小段,线程1、线程2…分开存储在不同的段.text。各线程执行各自的代码。栈空间也会有不同,分成一个个小段分给各线程。
线程之间共享和非共享资源
信号掩码:每个线程的信号阻塞不一样
NPTL
Linux系统线程库的发展概述
查看当前线程库版本:
3.2 创建线程
线程操作
创建线程
出现报错及原因
线程是第三方库不是标准系统库,需要通过-l指定去指定库的名称
解决方案如下:加上第三方库 -pthread 或者 -lpthread
wcx@wcx-virtual-machine:~/linux/lesson29$ gcc pthread_create.c -o pthread_create -pthread
main函数中所有执行的代码为主线程的代码,子线程执行的代码为回调函数的代码
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
//函数指针,void*为万能指针
void * callback(void * arg) {
printf("child thread...\n");
printf("arg value: %d\n", *(int *)arg);
return NULL;
}
int main() {
pthread_t tid;
int num = 10;
// 创建一个子线程
int ret = pthread_create(&tid, NULL, callback, (void *)&num);
if(ret != 0) {
char * errstr = strerror(ret);
printf("error : %s\n", errstr);
}
for(int i = 0; i < 5; i++) {
printf("%d\n", i);
}
//预防子线程还在创建或子线程未抢占执行权,程序就退出,预防子线程的未被执行
sleep(1);
return 0; // exit(0);
}
显示结果:
3.3终止线程
让主线程退出,当主线程退出时,不会影响其他正常运行的线程(子线程)。因为到这一步主线程是就退出了,就没有执行return 0,相当于exit(0),所以进程还在,就没有影响其他子进程
pthread_exit(NULL); //因为到这一步主线程是就退出了,就没有执行return 0,相当于exit(0),所以进程还在,就没有影响其他子进程
//该句没有被执行
printf("main thread exit\n");
//没有执行
return 0; // exit(0);
#include <stdio.h>
#include <pthread.h>
#include <string.h>
void * callback(void * arg) {
printf("child thread id : %ld\n", pthread_self());
return NULL; //相当于 pthread_exit(NULL);终止子线程
}
int main() {
// 创建一个子线程
pthread_t tid;
int ret = pthread_create(&tid, NULL, callback, NULL);
if(ret != 0) {
char * errstr = strerror(ret);
printf("error : %s\n", errstr);
}
// 主线程
for(int i = 0; i < 5; i++) {
printf("%d\n", i);
}
printf("tid : %ld, main thread id : %ld\n", tid ,pthread_self());
// 让主线程退出,当主线程退出时,不会影响其他正常运行的线程(子线程)。
pthread_exit(NULL); //因为到这一步主线程是就退出了,就没有执行return 0,相当于exit(0),所以进程还在,就没有影响其他子进程
//该句没有被执行
printf("main thread exit\n");
//没有执行
return 0; // exit(0);
}
下图说明主线程和子线程是交替执行的
3.4连接已终止的线程
子进程一定是被父进程回收,子线程不一定需要被父线程回收,可以被任意线程回收,但一般是主线程回收子线程。如果子线程结束后不被回收,也会产生僵尸线程。
为何使用二级指针?
要想改变一级指针的值,函数调用传入参数为二级指针。
因为返回值是一个指针,想通过retval传出返回值,肯定要用指针。
int a = 0
void act(&a)(){}
肯定是这样才能改变a的值。
同理如果a是一个指针类型,形参理所当然就是二级指针
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
//全局变量,若为局部变量,栈空间数据,最后获取的是个随机的值
//线程退出时记得一定返回的是全局变量的值
int value = 10;
void * callback(void * arg) {
printf("child thread id : %ld\n", pthread_self());
// sleep(3);
// return NULL;
// int value = 10; // 局部变量
pthread_exit((void *)&value); // return (void *)&value;
}
int main() {
// 创建一个子线程
pthread_t tid;
int ret = pthread_create(&tid, NULL, callback, NULL);
if(ret != 0) {
char * errstr = strerror(ret);
printf("error : %s\n", errstr);
}
// 主线程
for(int i = 0; i < 5; i++) {
printf("%d\n", i);
}
printf("tid : %ld, main thread id : %ld\n", tid ,pthread_self());
// 主线程调用pthread_join()回收子线程的资源
int * thread_retval;//定义一个一级指针
//传递一级指针的地址作为二级指针,并转换为void **
ret = pthread_join(tid, (void **)&thread_retval);
if(ret != 0) {
char * errstr = strerror(ret);
printf("error : %s\n", errstr);
}
printf("exit data : %d\n", *thread_retval);
printf("回收子线程资源成功!\n");
// 让主线程退出,当主线程退出时,不会影响其他正常运行的线程。
pthread_exit(NULL);
return 0;
}