线程 简介线程创建线程等待线程终止
简单来说线程就是进程的一个分支,在进程内部运行,这个被创建出来的线程在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 <stdio.h>#include <stdlib.h>#include <pthread.h>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终止自己。运行结果: