线程概念
Linux操作系统并不是真线程操作系统,意味着没有专门描述组织线程的内核数据结构,所有线程都是通过进程PCB模拟的.
什么是进程,进程如何模拟线程?
进程概念:
进程是系统分配程序资源的基本单位,如虚拟内存,页表,文件描述符表,信号集...这些资源都是按照进程为单位来分配的!
cpu拿到进程PCB就可以通过它指向的进程资源(虚拟内存,页表等),找到程序的代码和数据.所以PCB就称作一个执行流.
那一个进程可不可有多个PCB指向相同的系统资源呢?
当然可以,而这样的做,就是线程的基本原理!
线程概念:
线程是cpu调度的基本单位,而一个进程下可以有多个线程,他们共用同一套进程资源!
结合这张图和上述概念,一些书上的概念就很容易理解了,什么是线程:
1.在进程内部运行的执行流
2.线程比进程的粒度更细,调度成本更低(线程不需要像切换进程那样切换进程资源)
3.线程是CPU调度的基本单元
创建和等待线程
pthread库
由于系统提供的接口过于复杂,所以除去部分语言自带的线程库之外,普遍使用pthread库来做线程相关的操作.
pthread虽然是第三方库,但却是系统自带的,不需要手动下载安装,使用时只需要包含头文件pthread.h 编译时添加选项 -lpthread 即可.
本文用最简单c语言,使用pthread库完成线程的相关操作
创建线程
参数1:pthread_t 是库中提供的线程id类型,此处为输出型参数,需传入一个pthread_t的地址来接收新建线程的id.
参数2: 用来设置特殊属性,一般情况设置为nullptr.
参数3:传入要线程执行的函数,(可以理解为线程的主函数)注意返回值和参数都是void*类型
参数4:参数3函数的参数,void*类型,传参很灵活.
返回值:创建成功返回0,否则返回的错误码.
创建成功后,main函数对应的执行流就是主线程,其他的就称为子线程. 主线程return 代表进程退出,所有的线程都会被退出,进程的资源会被回收.
代码案例:
//头文件 pthread.h
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *thread_routine(void *argc)
{
printf("%s : start running\n",(char*)argc);
//other work
sleep(1);
//exit
printf("exit\n");
return NULL;
}
int main()
{
pthread_t tid;
pthread_create(&tid,NULL,thread_routine,(void*)"thread_1");
sleep(5);
return 0;
}
线程退出有2种方式,
1.return 返回值
2.pthread_exit(返回值)
void *thread_routine(void *argc)
{
printf("%s : start running\n",(char*)argc);
//other work
sleep(1);
//exit
printf("exit\n");
//退出方式1
//return NULL;
//退出方式2
//pthread_exit(NULL);
}
只有通过线程等待,才能获取线程的返回值!
线程等待
参数1:要等待的线程id.
参数2:输出型参数,用来接收线程的返回值. 不需要获取可以传nullptr
调用这个函数,会阻塞等待线程退出,获取线程返回值 ,并回收线程资源!
所以上面的代码案例是有问题的,会导致线程资源没有被主线程回收,造成内核资源泄漏.
改进后:
//头文件 pthread.h
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *thread_routine(void *argc)
{
printf("%s : start running\n", (char *)argc);
// other work
sleep(1);
// exit
printf("exit\n");
//退出方式1
// return NULL;
//退出方式2
pthread_exit(NULL);
}
int main()
{
pthread_t tid_1, tid_2, tid_3;
pthread_create(&tid_1, NULL, thread_routine, (void *)"thread_1");
pthread_create(&tid_2, NULL, thread_routine, (void *)"thread_2");
pthread_create(&tid_3, NULL, thread_routine, (void *)"thread_3");
void *thread_ret; //输出型参数
pthread_join(tid_1, &thread_ret); //阻塞等待
printf("join succeed return value:%d\n", (long int)thread_ret);
pthread_join(tid_2, &thread_ret); //阻塞等待
printf("join succeed return value:%d\n", (long int)thread_ret);
pthread_join(tid_3, &thread_ret); //阻塞等待
printf("join succeed return value:%d\n", (long int)thread_ret);
return 0;
}