本文已参与「新人创作礼」活动,一起开启掘金创作之路。
线程概念
大条件:
Linux中:
因为Linux环境下线程以进程的PCB模拟实现线程,所以Linux中PCB是线程。因此Linux线程也叫轻量级进程。
进程就是线程组。以前所描述的进程其实是单线程进程。
因为Linux线程是PCB,因此线程是CPU调度的基本单位。
因为进程是线程组,程序运行起来,资源是分配给整个线程组的,因此进程是资源分配的基本单位。
多进程可以并行多任务 / 多线程也可以并行多任务,何者更优?
多进程与多线程优缺点对比
- 优点: 一个进程中的线程共用同一个虚拟地址空间,因此:
- 线程间通信更加方便。
- 线程的创建 / 销毁成本更低。
- 线程间切换调度成本更低。
- 线程的执行粒度更细致。
- 缺点:
线程缺乏访问控制:系统调用(如
exit()),异常针对的是整个进程,健壮性低。
多进程 / 多线程进行多任务处理的优势体现与细节:
CPU密集型程序IO密集型程序
vfork()创建一个子进程共用同一块虚拟地址空间,避免出现调用栈混乱,因此子进程运行完毕或程序替换后父进程才开始运行。
多线程PCB使用同一块虚拟地址空间,如何实现同步运行而不会出现调用栈混乱?
为每个线程在虚拟地址空间中单独分配一块空间,每个线程都会有一些独立的信息(栈、寄存器、errno、信号屏蔽字、调度优先级)
线程之间共享的数据:代码段、数据段、文件描述符表、每种信号的处理方式、用户id、组id、当前工作目录路径。
线程控制
操作系统并没有为用户提供直接创建线程的系统调用接口,因此提供者封装了一套线程库,实现线程控制。 因此我们用户创建的线程是一个用户态线程,在内核态中对应了一个轻量级进程,实现程序的调度运行。
线程创建
pthread_create()接口
需要包含的头文件<pthread.h>,因为是库函数,因此链接时需要加上-pthread或-lpthread选项来链接线程库。
int pthread_create(
pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg
);
thread:输出型参数,用于获取新创建的线程tid。attr:设置线程属性,通常置NULL。start_routine:线程的入口函数,是一个函数指针。arg:作为实参,通过线程入口函数传递给线程。- 返回值:
成功返回
0。失败返回errno,它的大小>0。
区别:
tid:线程地址空间的首地址。task_struct中的pid:LWP。task_struct中的tgid:线程组id(主线程的pid)(也是进程id)。
ps -L:查看轻量级进程(线程)信息。其中LWP查看的就是task_struct中的pid。
pthread_self()接口
返回调用线程的id。
==线程创建实例==
void *thr_start(void *arg){
//pthread_t pthread_self(void);
// 返回调用线程id
pthread_t tid = pthread_self();
while(1) {
printf("i am thread~~~%s-%p\n", (char*)arg, tid);
sleep(1);
}
return NULL;
}
int main(int argc, char *argv[]){
pthread_t tid;
int ret = pthread_create(&tid, NULL, thr_start, (void*)"giturtle");
if (ret != 0) {
printf("thread create error\n");
return -1;
}
printf("main tid:%p\n", pthread_self());
printf("child tid:%p\n", tid);
while(1) {
printf("hello world~~~\n");
sleep(1);
}
return 0;
}
运行结果
main tid:0x7ff449b99740
child tid:0x7ff4493a7700
hello world
i am thread~~nihao-0x7ff4493a7700
hello world
i am thread~~nihao-0x7ff4493a7700
hello world
i am thread~~nihao-0x7ff4493a7700
hello world
i am thread~~nihao-0x7ff4493a7700
hello world
i am thread~~nihao-0x7ff4493a7700
hello world
...
线程终止
-
在线程入口函数中
return。main函数中不能return,否则退出的是进程。 -
使用接口退出调用线程:
pthread_exit()接口
pthread_exit(void *retval);
pthread_exit(hello!);
退出调用线程,主线程退出,进程并不会退出,等所有线程退出进程才会退出。
线程退出也会成为僵尸线程(普通线程退出无效果),线程也要保存返回值,线程地址空间无法被回收再利用,造成资源泄露。
- 使用接口取消一个指定的线程:
pthread_cancel()接口
pthread_cancel(tid);
取消一个指定线程,方式为被动退出。
【注】如果一个线程是被取消的,则返回值是一个宏PTHREAD_CANCELED,它的值是-1。
==线程终止实例==
void *thr_start(void *arg){
while(1) {
printf("child thread\n");
sleep(1);
//或者使用 pthread_exit(NULL);退出线程
}
return NULL;
}
int main(int argc, char *argv[]){
pthread_t tid;
int ret = pthread_create(&tid, NULL, thr_start, NULL);
if (ret != 0) {
printf("thread create error\n");
return -1;
}
pthread_cancel(pthread_self());
while(1) {
printf("main thread\n");
sleep(1);
}
return 0;
}
运行结果
main thread
child thread