本文共 3042 字,大约阅读时间需要 10 分钟。
简单来说线程就是
进程的一个分支
,在进程内部运行,这个被创建出来的线程在Linux系统下可以看作是一个轻量级的进程(LWP)
,一旦一个轻量级进程被创建出来,他与父进程共用
同一份代码和数据,为什么说是轻量级进程呢,要知道在Linux系统下,并没有描述线程的这一结构体,所以并没有真正意义上的线程,有的只是用进程模拟出来
的线程,为什么说模拟出来的,因为他有自己的PCB,但他不会拷贝
父进程的地址空间和页表,而是在父进程的地址空间内部运行。所以可以想来,他与父进程公用一份代码和数据,这样设置的好处是让线程的运行更加流畅,移植性更好。了解了以上Linux平台下对线程介绍后,我们发现,所有PCB代表的进程都可以称之为轻量级进程(有可能该进程只是一个进程),
进程变为承担分配系统资源的基本实体,而线程是CPU/操作系统调度的基本单位(存在多个执行流时才需要调度)。
呢为什么既要有进程,又要有线程呢?
设置进程为的是强调资源的独享,而设置线城市为了强调资源的共享
。
一个进程中的多个线程共享什么资源呢
- 文件描述符表
- 每种信号的处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数)
- 当前公作目录
- 用户id和组id
但线程也需要自己的自己的自由信息,例如在线程中产生的临时变量。这些临时变量存放在每个线程专属的私有栈之中,除了这些临时变量但有些资源是每个线程各有1份的:
- 线程id
- 上下文,包括各种寄存器的值、程序计数器和栈指针
- 栈空间
- errno变量
- 信号屏蔽字
调度优先级
注意
线程库函数是由POSIX标准定义的,称为POSIX thread或者pthread。在Linux
上线程函数位于libpthread共享库中,因此在编译时要加上-lpthread选项
。
返回值:成功返回0,失败返回
错误号
。错误信号保存在全局变量errno中,而pthread库的函数都是通过返回值返回错误号,虽然每个线程也都有1个errno,但这是为了兼容其它函数接口而提供的,pthread库本身并不使用它,通过返回值返回错误码更加清晰。在多个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,当新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。start_routine函数接收一个参数,是通过pthread_create的
arg
参数传递给它的,该参数的类型为void ,这个指针按什么类型解释由调度者自己定义。start_routine的返回值类型也是void ,这个指针的含义同样由调度者自己定义。start_routine返回时,这个线程就退出了,其它线程可以调用pthread_join得到start_routine的返回值,类似于父进程调用wait(2)得到子进程的退出状态。
pthread_create成功返回后,新创建的线程的id被填写到thread参数所指向的内存单元。我们知道进程id的类型是pid_t,每个进程的id在整个系统中是唯一的,调用getpid(2)可以获得当前进程的id,是一个正整数值。线程id的类型是thread_t,它只在当前进程中保证是唯一的,在不同的系统中thread_t这个类型有不同的实现,它可能是一个整数值,也可能是一个结构体,也可能是一个地址,所以不能简单地当成整数用printf打印,调用pthread_self(3)可以获得当前线程的id,attr
参数表示线程属性。
返回值:成功返回0,失败返回错误号
调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:
- 如果thread线程通过return返回,value_ptr所指向的单元内存放的是thread线程函数的
返回值
。- 如果thread线程被别的线程调用pthread_cancel异常终掉,value_ptr所指向的单元内存放的是常数
PTHREAD_CANCELED
。(常数PTHREAD_CANCELED的值是-1。可以在头文件pthread.h中找到它的定义)- 如果thread线程是由于调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数。
#include#include #include void* thread1(void* _val) { printf("thrad 1 returning...\n"); return (void*)1; } void* thread2(void* _val) { printf("thread 2 returning...\n"); pthread_exit((void*)2); } void* thread3(void* _val) { while(1) { printf("pthread 3 is runing\n"); sleep(1); } return NULL; } int main() { pthread_t tid; void* tret; //thread_join pthread_create(&tid, NULL, thread1,NULL); pthread_join(tid, &tret); printf("thread1 return, thread id is : %u, return code is : %d\n", tid, (int)tret); //thread_exit pthread_create(&tid, NULL, thread2,NULL); pthread_join(tid, &tret); printf("thread2 return, thread id is : %u, exit code is : %d\n", tid, (int)tret); //thread 3 cancel by main pthread_create(&tid, NULL, thread3,NULL);
retval
是void *
类型,和线程函数返回值的用法一样,其它线程可以调用pthread_join获得这个指针。
需要注意
,pthread_exit或者return返回的指针所指向的内存单元必须是全局的
或者是用malloc分配的
,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了,这意味着函数内部所有临时变量已经销毁。
如果需要只终止某个线程而不终止整个进程,可以有三种方法:
- 从线程函数return。这种方法对主线程不适合,从main函数return
相当于调用exit
。- 单个线程可以调用pthread_cancel终止同一进程中的
另一个线程
。- 线程可以调用pthread_exit
终止自己
。运行结果: