一、线程的概念:

   线程是运行在进程地址空间的,是进程的执行分流。它是执行进程内部的一个代码片段为了让进程更好的完成。也可以说,进程是承担系统资源的实体,而线程进程运行调度的基本单位。

   由于同一进程的多个线程共享同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境: 

1. 文件描述符表

2. 每种信号的处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数)

3. 当前工作目录

4. 用户id和组id 

但有些资源是每个线程各有一份的: 

  1. 线程id

  2. 硬件上下文,包括各种寄存器的值、程序计数器和栈指针

  3. 栈空间

  4. errno变量

  5. 信号量屏蔽字

  6. 调度优先级

在这里我学习的是POSIX标准定义的,称为POSIX thread或者pthread。在Linux上线程函数位于libpthread共享库中,因此在编译时要加上-lpthread选项。

二、线程控制

(1)、创建线程

创建线程的函数如下:

wKiom1cTSCGwtZjxAAAruthyWvY464.jpg

wKioL1cTScXTNketAAAfKCNHNVc570.jpg

   以前学过的系统函数都是成功返回0,失败返回-1,错误号保存在全局变量errno中,pthread库的函数都是通过返回值返回错误号,虽然每个线程也都有 一个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()可以获得当前进程

的id,是一个正整数值。线程id的类型是thread_t,它只在当前进程中保证是唯一的,在不同的系

统中thread_t这个类型有不同的实现,它可能是一个整数值,也可能是一个结构体,也可能是一个地址,所以不能简单地当成整数。printf打印,调用pthread_self()可以获得当前线程的id。attr参数表示线程属性。


wKiom1cTUqCza0EyAABsBqQgA7w574.jpg

其运行结果为:

wKiom1cTU1uhC_ahAAAmVNR6rlE376.jpg

三、线程终止

(1).在线程函数中直接return

(2).在线程函数中调用pthread_exit()

(3).在函数内部调用pthread_cancel(pthread_self)取消或被其它线程取消pthread_cancel(tid);

四。线程等待

wKiom1cTVpazvE1IAAAXoQ3y1x0379.jpg

返回值:成功返回0,失败返回错误号  

调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

1. 如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值。

2. 如果thread线程被别的线程调用pthread_cancel异常终掉,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED。

3. 如果thread线程是被别的线程调pthread_exit终止的,value_ptr所指向的单元存放的是传给

pthread_exit的参数。如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数。


wKioL1cTWW7iQ8RCAAAXoQ3y1x0867.jpg

wKioL1cTWeqDegq1AACT_lCyZsg538.jpg

wKiom1cTWTeixZkSAABGrl-e8BY833.jpg

其运行结果为:

wKioL1cTWkvTVfFeAAA1ctdJ3VM418.jpg

四、线程分离

线程分离函数:

wKiom1cTW1DgMnQBAAAULVmdWo0725.jpg

   在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached)。一个可

结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源

(例如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动动释放。

   默认情况下,线程被创建成可结合的。为了避免存储器泄漏,每个可结合线程都应该要么被显示地回收,即调用pthread_join;要么通过调用pthread_detach函数被分离。

  如果一个可结合线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收,所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源。

    由于调用pthread_join后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我

们并不希望如此。例如,在Web服务器中当主线程为每个新来的连接请求创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join阻塞(因为还要继续处理之后到来的连接请求),这时可以在子线程中加入代码

    pthread_detach(pthread_self()) 

或者父线程调用

 pthread_detach(thread_id)(非阻塞,可立即返回)

这将该线程的状态设置为分离的(detached),如此一来,该线程运行结束后会自动释放资源。

   

不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL。 对一个尚detach的线程调用pthread_join或pthread_detach都可以把该线程置为detach状态,也就是说,不能对同一线程调用两次pthread_join,或者如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。

 

   wKiom1cTYinDYSPNAACXSI7QrcY030.jpg

运行结果为:

wKioL1cTYvuQdnPAAAAY7fHM4mQ300.jpg