本文谢绝转载原文来自http://990487026.blog.51cto.com


<大纲>
	Linux系统开发8 线程
		线程概念
		浏览器 火狐多线程,谷歌多进程比较:
		查看某一个进程有哪些线程
		线程间共享资源
		线程间非共享资源
		线程优缺点
		安装完整的manpage文档
		pthread_create()创建线程
		pthread_self() 获取线程自己的ID
		线程创建程序演示:
		指定libpthread.so库编译链接
		演示:进程结束,线程也会立即结束
		pthread_exit() 调用线程退出函数
		pthread_join()回收线程的资源
		pthread_cancel() 在进程内某个线程可以终止另一个线程,依然是join()回收线程资源
		pthread_detach()分离线程tid,资源由系统自动管理,不能使用join()回收
		pthread_join() pthread_exit() pthread_cancel() 线程程序演示
		pthread_join()一个线程不能回收两次
		把线程转成分离态,分离态的线程不能pthread_join()
		线程终止的几种方式
		线程pthread_cancel()终止方式,没有执行系统调用不会立即终止线程
		线程pthread_cancel()终止方式,执行系统调用会立即终止线程
		查看linux系统栈大小
		测试最大线程数
		创建一个线程,变成分状态的线程,僵尸线性
		设置线程属性,线程栈
		设置线程属性,为线程分配占空间,批量创建
		LIBPTHREAD_VERSION查看
		细节注意
		练习		
		



线程概念

什么是线程,线程和进程的关系

1.轻量级进程(light-weight process),也有PCB,创建线程使用的底层函数和进程一样,都是clone

2.从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存资源的三级页表是相同的

3.进程可以蜕变成线程

4.在美国人眼里,线程就是寄存器和栈

5.在linux下,线程最是小的执行单位;进程是最小的分配资源单位





浏览器 火狐多线程,谷歌多进程比较:

火狐浏览器,打来3个标签页,浏览网页内容:

老师说火狐是多线程,可以看出来都是同一个PID 16778,多个LWP

chunli@ubuntu:~$ ps -eLf  | head -n 1; ps -eLf  |grep firefox
UID         PID   PPID    LWP  C NLWP STIME TTY          TIME CMD
chunli    16778  15190  16778 44   53 09:50 ?        00:00:19 /usr/lib/firefox/firefox
chunli    16778  15190  16785  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16786  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16787  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16792  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16793  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16794  8   53 09:50 ?        00:00:03 /usr/lib/firefox/firefox
chunli    16778  15190  16795  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16796  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16797  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16798  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16799  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16800  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16801  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16802  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16803  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16804  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16805  1   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16806  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16807  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16808  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16812  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16813  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16817  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16819  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16822  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16823  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16824  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16825  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16826  1   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16827  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16828  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16829  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16832  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16833  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16834  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16835  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16836  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16837  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16838  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16840  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16841  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16842  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16844  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16845  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16846  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16847  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16848  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16849  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16850  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16851  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16852  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16778  15190  16853  0   53 09:50 ?        00:00:00 /usr/lib/firefox/firefox
chunli    16875  16044  16875  0    1 09:50 pts/22   00:00:00 grep --color=auto firefox
chunli@ubuntu:~$




谷歌浏览器,打来3个标签页,浏览网页内容:

老师说火狐是多进程,可以看出来都是同多个PID ,多个LWP

chunli@ubuntu:~$ ps -eLf  | head -n 1; ps -eLf  |grep chrom
UID         PID   PPID    LWP  C NLWP STIME TTY          TIME CMD
chunli    16188  15190  16188  1   38 09:39 ?        00:00:06 chromium-browser --enable-pinch
chunli    16188  15190  16198  0   38 09:39 ?        00:00:01 chromium-browser --enable-pinch
chunli    16188  15190  16203  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16204  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16205  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16206  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16208  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16209  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16210  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16211  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16212  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16213  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16214  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16215  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16216  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16217  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16218  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16219  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16220  0   38 09:39 ?        00:00:03 chromium-browser --enable-pinch
chunli    16188  15190  16221  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16222  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16223  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16224  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16225  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16298  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16299  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16300  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16303  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16343  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16361  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16403  0   38 09:39 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16690  0   38 09:48 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16691  0   38 09:48 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16692  0   38 09:48 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16699  1   38 09:48 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16700  1   38 09:48 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16701  1   38 09:48 ?        00:00:00 chromium-browser --enable-pinch
chunli    16188  15190  16737  2   38 09:48 ?        00:00:00 chromium-browser --enable-pinch
chunli    16200  16188  16200  0    1 09:39 ?        00:00:00 chromium-browser --type=zygote
chunli    16202  16200  16202  0    1 09:39 ?        00:00:00 chromium-browser --type=zygote
chunli    16689  16202  16689  7    9 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16689  16202  16693  0    9 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16689  16202  16694  0    9 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16689  16202  16695  0    9 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16689  16202  16696  0    9 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16689  16202  16697  0    9 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16689  16202  16698  0    9 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16689  16202  16702  0    9 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16689  16202  16704  0    9 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16706  16202  16706 44   14 09:48 ?        00:00:03 /usr/lib/chromium-browser/chro
chunli    16706  16202  16707  3   14 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16706  16202  16708  0   14 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16706  16202  16709  0   14 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16706  16202  16710  0   14 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16706  16202  16711  0   14 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16706  16202  16712  0   14 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16706  16202  16713  0   14 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16706  16202  16723  0   14 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16706  16202  16724  0   14 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16706  16202  16726  0   14 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16706  16202  16727  0   14 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16706  16202  16728  0   14 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16706  16202  16729  0   14 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16714  16202  16714 54   11 09:48 ?        00:00:03 /usr/lib/chromium-browser/chro
chunli    16714  16202  16715  1   11 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16714  16202  16716  0   11 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16714  16202  16717  0   11 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16714  16202  16718  0   11 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16714  16202  16719  0   11 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16714  16202  16720  0   11 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16714  16202  16736  0   11 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16714  16202  16738  0   11 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16714  16202  16739  0   11 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16714  16202  16740  0   11 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16721  16202  16721 44   10 09:48 ?        00:00:01 /usr/lib/chromium-browser/chro
chunli    16721  16202  16722  3   10 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16721  16202  16725  0   10 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16721  16202  16730  0   10 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16721  16202  16731  0   10 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16721  16202  16732  0   10 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16721  16202  16733  0   10 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16721  16202  16734  0   10 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16721  16202  16735  0   10 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16721  16202  16741  1   10 09:48 ?        00:00:00 /usr/lib/chromium-browser/chro
chunli    16745  16044  16745  0    1 09:48 pts/22   00:00:00 grep --color=auto chrom
chunli@ubuntu:~$



查看某一个进程有哪些线程:

1,比如我想看会话信息
chunli@ubuntu:~$ ps -eLf | grep session-child
UID         PID   PPID    LWP  C NLWP STIME TTY          TIME CMD
root      16952   3759  16952  0    3 09:57 ?        00:00:00 lightdm --session-child 16 19
root      16952   3759  16953  0    3 09:57 ?        00:00:00 lightdm --session-child 16 19
root      16952   3759  16954  0    3 09:57 ?        00:00:00 lightdm --session-child 16 19
root      17018   3759  17018  0    1 09:57 ?        00:00:00 lightdm --session-child 12 19
chunli    17178   4619  17178  0    1 10:01 pts/8    00:00:00 grep --color=auto session-child
chunli@ubuntu:~$ 

2,指定会话的PID
chunli@ubuntu:~$ ps -Lw 16952
   PID    LWP TTY      STAT   TIME COMMAND
 16952  16952 ?        Sl     0:00 lightdm --session-child 16 19
 16952  16953 ?        Sl     0:00 lightdm --session-child 16 19
 16952  16954 ?        Sl     0:00 lightdm --session-child 16 19
chunli@ubuntu:~$



线程间共享资源

1.文件描述符表

2.每种信号的处理方式

3.当前工作目录

4.用户ID和组ID

5.内存地址空间 Text  data bss 堆 共享库



线程间非共享资源


1.线程id

2.处理器现场和栈指针(内核栈)

3.独立的栈空间(用户空间栈)

4.errno变量

5.信号屏蔽字

6.调度优先级



线程优缺点


【优点】

提高程序的并发性

开销小,不用重新分配内存

通信和共享数据方便


【缺点】

线程不稳定(库函数实现)

线程调试比较困难(gdb支持不好)

线程无法使用unix经典事件,例如信号



安装完整的manpage文档

chunli@ubuntu:~$ sudo apt-get install -y manpages  manpages-dev 
chunli@ubuntu:~$ sudo apt-get install -y manpages-posix manpages-posix-dev

manpages 包含 GNU/Linux 的基本操作
manpages-dev 包含 GNU/Linux 的基本操作API
manpages-posix 包含 POSIX 所定义公用程序的方法
manpages-posix-dev 包含 POSIX 的 header files 和 library calls 的用法


pthread_create()创建线程


int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);


pthread_t *thread:传递一个pthread_t变量地址进来,用于保存新线程的tid(线程ID)

const pthread_attr_t *attr:线程属性设置,如使用默认属性,则传NULL

void *(*start_routine) (void *):函数指针,指向新线程应该加载执行的函数模块

void *arg:指定线程将要加载调用的那个函数的参数

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


以前学过的系统函数都是成功返回0,失败返回-1,而错误号保存在全局变量errno中,

而pthread库的函数都是通过返回值返回错误号,虽然每个线程也都有一个errno,

但这是为了兼容其它函数接口而提供的,pthread库本身并不使用它,通过返回值返回错误码更加清晰。


Compile and link with -lpthread.


typedef unsigned long int pthread_t;


在一个线程中调用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_join。


pthread_create成功返回后,新创建的线程的id被填写到thread参数所指向的内存单

元。我们知道进程id的类型是pid_t,每个进程的id在整个系统中是唯一的,

调用getpid(2)可以获得当前进程的id,是一个正整数值。

线程id的类型是thread_t,它只在当前进程中保证是唯一的,在不同的系统中thread_t这个类型有不同的实现,

它可能是一个整数值,也可能是一个结构体,也可能是一个地址,所以不能简单地当成整数用printf打印,

调用pthread_self(3)可以获得当前线程的id。

attr参数表示线程属性,本节不深入讨论线程属性,所有代码例子都传NULL给attr参数,

表示线程属性取缺省值,感兴趣的读者可以参考[APUE2e]。




pthread_self() 获取线程自己的ID


#include

pthread_t pthread_self(void);


由于pthread_create的错误码不保存在errno中,因此不能直接用perror(3)打印错误信

息,可以先用strerror(3)把错误码转换成错误信息再打印。

如果任意一个线程调用了exit或_exit,则整个进程的所有线程都终止,由于从main函

数return也相当于调用exit,为了防止新创建的线程还没有得到执行就终止,我们在main函

数return之前延时1秒,这只是一种权宜之计,即使主线程等待1秒,内核也不一定会调度新

创建的线程执行,下一节我们会看到更好的办法。




线程创建程序演示:

chunli@ubuntu:~/linux_c/thread$ cat pthread.c 
#include 
#include 
#include 
#include 

void *th_fun(void *n)
{
	printf("thread PID = %d\n",getpid());		//getpid() returns the process ID of the calling process.
	printf("thread ID = %ld\n",pthread_self());	//This function always succeeds, returning the calling thread's ID.
	printf("thread n = %d\n",*(int *)n);
	while(1)
	{
		sleep(1);
	}
}

int main()
{
	pthread_t tid;
	int n = 10;
//	函数原型
//	#include 
//	int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

	pthread_create(&tid,NULL,th_fun,(void*)&n);
	printf("main thrdad ID = %ld\n",pthread_self());
	printf("main child thrdad ID = %ld\n",tid);
	printf("main PID = %d\n",getpid());
	while(1)
	{
		sleep(1);
	}
	return 0;
}





指定libpthread.so库编译链接

chunli@ubuntu:~$ sudo find / -iname *libpthread.so* 2>> /dev/null | grep libpthread.so 
/usr/lib/x86_64-linux-gnu/libpthread.so
/lib/i386-linux-gnu/libpthread.so.0
/lib/x86_64-linux-gnu/libpthread.so.0

chunli@ubuntu:~/linux_c/thread$ gcc pthread.c  -lpthread && ./a.out 
main thrdad ID = 140620739671808
main child thrdad ID = 140620731340544
main PID = 24697
thread PID = 24697
thread ID = 140620731340544
thread n = 10


chunli@ubuntu:~$ ps -eLf |head -n 1 ;  ps -eLf | grep a.out
UID         PID   PPID    LWP  C NLWP STIME TTY          TIME CMD
chunli    24655   6016  24655  0    2 11:46 pts/9    00:00:00 ./a.out
chunli    24655   6016  24656  0    2 11:46 pts/9    00:00:00 ./a.out
chunli    24683  17152  24683  0    1 11:47 pts/12   00:00:00 grep --color=auto a.out
chunli@ubuntu:~$ ps -Lw 24655
   PID    LWP TTY      STAT   TIME COMMAND
 24655  24655 pts/9    Sl+    0:00 ./a.out
 24655  24656 pts/9    Sl+    0:00 ./a.out
chunli@ubuntu:~$


演示:进程结束,线程也会立即结束

chunli@ubuntu:~/linux_c/thread$ cat pthread.c 
#include 
#include 
#include 
#include 

void *th_fun(void *n)
{
	printf("thread PID = %d\n",getpid());		//getpid() returns the process ID of the calling process.
	printf("thread ID = %ld\n",pthread_self());	//This function always succeeds, returning the calling thread's ID.
	printf("thread n = %d\n",*(int *)n);
	sleep(1);
	printf("Hello Linux!\n");
}

int main()
{
	pthread_t tid;
	int n = 10;
//	函数原型
//	#include 
//	int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

	pthread_create(&tid,NULL,th_fun,(void*)&n);
	printf("main thrdad ID = %ld\n",pthread_self());
	printf("main child thrdad ID = %ld\n",tid);
	printf("main PID = %d\n",getpid());
	return 0;
}
chunli@ubuntu:~/linux_c/thread$ gcc pthread.c  -lpthread && ./a.out 
main thrdad ID = 139956336580352
main child thrdad ID = 139956328249088
main PID = 24716
chunli@ubuntu:~/linux_c/thread$ 

线程的printf("Hello Linux!\n");的这句话没有来得及执行



pthread_exit() 调用线程退出函数


注意和exit函数的区别,任何线程里exit导致进程退出,其他线程

未工作结束,主控线程退出时不能return或exit。

需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是

用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函

数已经退出了。

#include

void pthread_exit(void *retval);

void *retval:线程退出时传递出的参数,可以是退出值或地址,如是地址时,不能是线程内部申请的局部地址。



pthread_join()回收线程的资源


#include

int pthread_join(pthread_t thread, void **retval);

pthread_t thread:回收线程的tid

void **retval:接收退出线程传递出的返回值

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

调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法

终止,通过pthread_join得到的终止状态是不同的,总结如下:

如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返

回值。

如果thread线程被别的线程调用pthread_cancel异常终止掉,retval所指向的单元里存

放的是常数PTHREAD_CANCELED。

如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给

pthread_exit的参数。

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




pthread_cancel() 在进程内某个线程可以终止另一个线程,依然是join()回收线程资源

#include

int pthread_cancel(pthread_t thread);

被取消的线程,退出值,定义在Linux的pthread库中常数PTHREAD_CANCELED的值是-1。

可以在头文件pthread.h中找到它的定义:

#define PTHREAD_CANCELED ((void *) -1)




pthread_detach()分离线程tid,资源由系统自动管理,不能使用join()回收


#include

int pthread_detach(pthread_t tid);

pthread_t tid:分离线程tid

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

一般情况下,线程终止后,其终止状态一直保留到其它线程调用pthread_join获取

它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收

它占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用

pthread_join,这样的调用将返回EINVAL。如果已经对一个线程调用了pthread_detach就不

能再调用pthread_join了。




pthread_join() pthread_exit() pthread_cancel() 线程程序演示

chunli@ubuntu:~/linux_c/thread$ cat pthread_join.c 
#include 
#include 
#include 
#include 
void *thr_fn1(void *arg)
{
	printf("thread 1 returning\n");
	return (void *)1;
}
void *thr_fn2(void *arg)
{
	printf("thread 2 exiting\n");
	pthread_exit((void *)2);
}
void *thr_fn3(void *arg)
{
	while(1) {
		printf("thread 3 waiting\n");sleep(1);
	}
}
int main(void)
{
	pthread_t tid;
	void *tret;	//64位下有8个字节
	pthread_create(&tid, NULL, thr_fn1, NULL);
	pthread_join(tid, &tret);
	//printf("thread 1 exit code %d\n", (int)tret);	//原32bit程序
	printf("thread 1 exit code %zd\n", (size_t)tret);
	pthread_create(&tid, NULL, thr_fn2, NULL);
	pthread_join(tid, &tret);
	printf("thread 2 exit code %zd\n", (size_t)tret);
	pthread_create(&tid, NULL, thr_fn3, NULL);
	sleep(3);
	pthread_cancel(tid);
	pthread_join(tid, &tret);
	printf("thread 3 exit code %zd\n", (size_t)tret);
	return 0;
}
//在32位下typedef unsigned int size_t,在64位下typedef unsigned long size_t。
//如果需要printf size_t类型的变量,会出现32/64下不兼容的情况,
//此时可以使用%Zd或者%zd。%zd是C99规定的,%Zd是GNU的扩展。
chunli@ubuntu:~/linux_c/thread$ gcc pthread_join.c  -lpthread -Wall  && ./a.out 
thread 1 returning
thread 1 exit code 1
thread 2 exiting
thread 2 exit code 2
thread 3 waiting
thread 3 waiting
thread 3 waiting
thread 3 exit code -1
chunli@ubuntu:~/linux_c/thread$


pthread_join()一个线程不能回收两次

chunli@ubuntu:~/linux_c/thread$ cat pthread_detach.c
#include 
#include 
#include 
#include 
#include 
void *thr_fn(void *arg){
	int n = 3;
	while (n--) 
	{
		printf("thread count %d\n", n);
		sleep(1);
	}
	return (void *)1;
}
int main(void)
{
	pthread_t tid;
	void *tret;
	int err;
	pthread_create(&tid, NULL, thr_fn, NULL);
	while (1) 
	{
		err = pthread_join(tid, &tret);	//一个线程不能回收两次
		if (err != 0)
			fprintf(stderr, "thread %s\n", strerror(err));
		else
			fprintf(stderr, "thread exit code %zd\n", (size_t)tret);
		sleep(1);
	}
	return 0;
}
chunli@ubuntu:~/linux_c/thread$ gcc pthread_detach.c  -lpthread -Wall  && ./a.out 
thread count 2
thread count 1
thread count 0
thread exit code 1
thread No such process
thread No such process
thread No such process
^C
chunli@ubuntu:~/linux_c/thread$


把线程转成分离态,分离态的线程不能pthread_join()

chunli@ubuntu:~/linux_c/thread$ cat pthread_detach.c 
#include 
#include 
#include 
#include 
#include 
void *thr_fn(void *arg){
	int n = 3;
	while (n--) 
	{
		printf("thread count %d\n", n);
		sleep(1);
	}
	return (void *)1;
}
int main(void)
{
	pthread_t tid;
	void *tret;
	int err;
	pthread_create(&tid, NULL, thr_fn, NULL);
	//第一次运行时注释掉下面这行,第二次再打开,分析两次结果
	pthread_detach(tid);
	while (1) 
	{
		err = pthread_join(tid, &tret);	//一个线程不能回收两次
		if (err != 0)
			fprintf(stderr, "thread %s\n", strerror(err));
		else
			fprintf(stderr, "thread exit code %zd\n", (size_t)tret);
		sleep(1);
	}
	return 0;
}
chunli@ubuntu:~/linux_c/thread$ gcc pthread_detach.c  -lpthread -Wall  && ./a.out 
thread Invalid argument
thread count 2
thread Invalid argument
thread count 1
thread count 0
thread Invalid argument
thread Invalid argument
thread Invalid argument
thread Invalid argument
thread Invalid argument
thread Invalid argument
^C
chunli@ubuntu:~/linux_c/thread$



线程终止的几种方式


如果需要只终止某个线程而不终止整个进程,可以有三种方法:

1.从线程主函数return。这种方法对主控线程不适用,从main函数return相当于调用exit。

2.一个线程可以调用pthread_cancel终止同一进程中的另一个线程。

3.线程可以调用pthread_exit终止自己。


同一进程的线程间,pthread_cancel向另一线程发终止信号。

系统并不会马上关闭被取消线程,只有在被取消线程下次系统调用时,

才会真正结束线程。或调用pthread_testcancel,让内核去检测是否需要取消当前线程



线程pthread_cancel()终止方式,没有执行系统调用不会立即终止线程

chunli@ubuntu:~/linux_c/thread$ cat pthread_detach.c 
#include 
#include 
#include 
#include 
#include 
void *thr_fn(void *arg){
	int n = 0;
	while (1) 
	{
		n++;
		//sleep(1);
	}
}
int main(void)
{
	pthread_t tid;
	pthread_create(&tid, NULL, thr_fn, NULL);
	pthread_cancel(tid);
	while (1) 
	{
		sleep(1);
	}
	return 0;
}
chunli@ubuntu:~/linux_c/thread$ gcc pthread_detach.c  -lpthread -Wall  && ./a.out 

线程并没有终止,因为线程没有做任何系统调用
chunli@ubuntu:~$ ps -eLf | head -n 1 ; ps -eLF |grep a.out
UID         PID   PPID    LWP  C NLWP STIME TTY          TIME CMD
chunli    25330   6016  25330  0    2  4213  1304   1 16:01 pts/9    00:00:00 ./a.out
chunli    25330   6016  25331 99    2  4213  1304   3 16:01 pts/9    00:00:16 ./a.out
chunli@ubuntu:~$


线程pthread_cancel()终止方式,执行系统调用会立即终止线程

chunli@ubuntu:~/linux_c/thread$ cat pthread_detach.c 
#include 
#include 
#include 
#include 
#include 
void *thr_fn(void *arg){
	int n = 0;
	while (1) 
	{
		n++;
		sleep(1);
	}
}
int main(void)
{
	pthread_t tid;
	pthread_create(&tid, NULL, thr_fn, NULL);
	pthread_cancel(tid);
	while (1) 
	{
		sleep(1);
	}
	return 0;
}
chunli@ubuntu:~/linux_c/thread$ gcc pthread_detach.c  -lpthread -Wall  && ./a.out 

线程会被终止,因为线程做了系统调用
chunli@ubuntu:~$ ps -eLf | head -n 1 ; ps -eLF |grep a.out
UID         PID   PPID    LWP  C NLWP STIME TTY          TIME CMD
chunli    25342   6016  25342  0    1  4213  1616   3 16:03 pts/9    00:00:00 ./a.out
chunli@ubuntu:~$



查看linux系统栈大小

chunli@ubuntu:~/linux_c/thread$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 3749
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 3749
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
chunli@ubuntu:~/linux_c/thread$



测试最大线程数

chunli@ubuntu:~/linux_c/thread$ cat stack.c 
#include 
#include 
#include 
#include 
#include 
void *th_fun(void *argc)
{
	while(1)
	{
		sleep(1);
	}
}

int main()
{
	pthread_t thread_id;
	int i = 1;
	int err = 0;
	while(1)
	{
		err = pthread_create(&thread_id,NULL,th_fun,NULL);//没有属性,没有参数
		if(err != 0)
		{
			printf("最大线程数%d\n",i);
			printf("%s\n",strerror(err));
			exit(1);	//所有的线程都会结束
		}
		i++;
	}
	return 0;
}
chunli@ubuntu:~/linux_c/thread$ gcc stack.c  -lpthread -Wall  && ./a.out 
最大线程数3736
Resource temporarily unavailable
chunli@ubuntu:~/linux_c/thread$


系统最大线程数

chunli@ubuntu:~/linux_c/thread$ cat /proc/sys/kernel/threads-max
7498
chunli@ubuntu:~/linux_c/thread$ 


chunli@ubuntu:~/linux_c/thread$ ulimit -s 
8192

chunli@ubuntu:~/linux_c/thread$ ulimit -s 4096
chunli@ubuntu:~/linux_c/thread$ ulimit -s 
4096


Ubuntu16.04 X64 在命令行调整栈大小,没有用
老师在32bit下缩小ulimit -s的数值,线程量多一倍

chunli@ubuntu:~/linux_c/thread$ gcc stack.c  -lpthread -Wall  && ./a.out 
最大线程数3736
Resource temporarily unavailable
chunli@ubuntu:~/linux_c/thread$



创建一个线程,变成分状态的线程,僵尸线性

chunli@ubuntu:~/linux_c/thread$ cat pthread_attr_init.c 
#include 
#include 
#include 
#include 
#include 

void *th_fun(void *argc)
{
	int n = 10;
	while(n--)
	{
		printf("%zx  %d\n",(size_t)pthread_self(),n);
		sleep(1);
	}
	return (void*)1;	
}

int main()
{
	pthread_t tid;
	pthread_attr_t attr;
	int err = 0;
	pthread_attr_init(&attr);//initializes  the  thread  attributes  object  pointed  to by attr with default attribute values
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//Threads that are created using attr will be created in a detached state
	pthread_create(&tid,&attr,th_fun,NULL);// starts a new thread in the calling process
	err = pthread_join(tid,NULL);	 //waits for the thread specified by thread to terminate
	while(1)
	{
		if(err != 0)
		{	
			printf("detached状态的线程不能回收,会报错 %s\n",strerror(err));
			sleep(3); 
			pthread_exit((void*)1);
		}
	}

	return 0;
}

编译运行:
chunli@ubuntu:~/linux_c/thread$ gcc pthread_attr_init.c  -lpthread   && ./a.out 
detached状态的线程不能回收,会报错 Invalid argument
7f40f931e700  9
7f40f931e700  8
7f40f931e700  7
7f40f931e700  6
7f40f931e700  5
7f40f931e700  4
7f40f931e700  3
7f40f931e700  2
7f40f931e700  1
7f40f931e700  0
chunli@ubuntu:~/linux_c/thread$ 


另外一个窗口ps aux观测
chunli@ubuntu:~$ ps aux > /tmp/ps;head -n 1 /tmp/ps;grep a.out < /tmp/ps
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
chunli     5233  0.0  0.0  14716   760 pts/9    Sl+  20:58   0:00 ./a.out

chunli@ubuntu:~$ ps aux > /tmp/ps;head -n 1 /tmp/ps;grep a.out < /tmp/ps
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
chunli     5233  0.0  0.0      0     0 pts/9    Zl+  20:58   0:00 [a.out] 



或者:另外一个窗口ps ajx观测
chunli@ubuntu:~$ ps ajx > /tmp/ps;head -n 1 /tmp/ps;grep a.out < /tmp/ps
  PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
  4485   5261   5261   4485 pts/9      5261 Sl+   1000   0:00 ./a.out

  chunli@ubuntu:~$ ps ajx > /tmp/ps;head -n 1 /tmp/ps;grep a.out < /tmp/ps
  PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
  4485   5261   5261   4485 pts/9      5261 Zl+   1000   0:00 [a.out] 
  
  
可以看到变成了僵尸线程



设置线程属性,线程栈

chunli@ubuntu:~/linux_c/thread$ cat pthread_attr_setstack.c 
#include 
#include 
#include 
#include 
#include 

void *th_fun(void * argc)
{
	int i = 10;
	while(i--)
	{
		printf("线程开始干活 %d\n",i);
		sleep(1);
	}
}

int  main()
{
	pthread_t	tid;
	pthread_attr_t	attr;
	int err = 0;
	int detachstate = 0;
	size_t stacksize  = 0;
	void *stackaddr;
	pthread_attr_init(&attr);	//初始化
	pthread_attr_getstack(&attr,&stackaddr,&stacksize);	//returns the stack address and stack size attributes of the thread attributes object
	printf("stackadd = %p\n",stackaddr);
	printf("stacksize = %zd\n",stacksize);
	pthread_attr_getdetachstate(&attr,&detachstate);//returns the detach state attribute of the thread attributes object attr in the buffer pointed to by detachstate
	if(detachstate == PTHREAD_CREATE_DETACHED)
	{
		printf("thread detached \n");
	}
	else if(detachstate == PTHREAD_CREATE_JOINABLE)
	{
		printf("thread join\n");
	}
	pthread_create(&tid,&attr,th_fun,NULL);
	pthread_join(tid,NULL);	
	printf("回收线程OK\n");
	pthread_attr_destroy(&attr);//与初始化相反,就是销毁
	
						
	
	return 0;
}
chunli@ubuntu:~/linux_c/thread$ clear ; gcc pthread_attr_setstack.c  -lpthread   && ./a.out 
stackadd = (nil)
stacksize = 0
thread join
线程开始干活 9
线程开始干活 8
线程开始干活 7
线程开始干活 6
线程开始干活 5
线程开始干活 4
线程开始干活 3
线程开始干活 2
线程开始干活 1
线程开始干活 0
回收线程OK
chunli@ubuntu:~/linux_c/thread$




设置线程属性,为线程分配占空间,批量创建

chunli@ubuntu:~/linux_c/thread$ cat pthread_attr_setstack.c 
#include 
#include 
#include 
#include 
#include 

void *th_fun(void * argc)
{
	int i = 10;
	while(i--)
	{
		//printf("线程开始干活 %d\n",i);
		sleep(1);
	}
}

int  main()
{
	pthread_t	tid;
	pthread_attr_t	attr;
	int err = 0;
	int detachstate = 0;
	size_t stacksize  = 0;
	void *stackaddr;
	pthread_attr_init(&attr);	//初始化
	pthread_attr_getstack(&attr,&stackaddr,&stacksize);	//returns the stack address and stack size attributes of the thread attributes object
	printf("stackaddr = %p\n",stackaddr);
	printf("stacksize = %zd\n",stacksize);
	pthread_attr_getdetachstate(&attr,&detachstate);//returns the detach state attribute of the thread attributes object attr in the buffer pointed to by detachstate
	if(detachstate == PTHREAD_CREATE_DETACHED)
	{
		printf("thread detached \n");
	}
	else if(detachstate == PTHREAD_CREATE_JOINABLE)
	{
		printf("thread join\n");
	}
	/*	设置线程分离属性	*/
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
	int i = 0;
	while(1)
	{

		/*	在堆上分配内存,指定线程的起始地址	*/
		stackaddr =  malloc(81960);
		if(stackaddr == NULL)
		{
			printf("maollc erorr\n");
		}
		stacksize = 81960;
		/*	设置stack	*/
		pthread_attr_setstack(&attr,stackaddr,stacksize);
		/*	创建线程	*/
		err = pthread_create(&tid,&attr,th_fun,NULL);
		if(err != 0)
		{
			printf("一共创建了%d个线程\n",i);
			printf("%s\n",strerror(err));
			exit(1);
		}
		i++;
	}

	pthread_join(tid,NULL);	
	printf("回收线程OK\n");
	pthread_attr_destroy(&attr);//与初始化相反,就是销毁
	
	return 0;
}
chunli@ubuntu:~/linux_c/thread$ gcc pthread_attr_setstack.c  -lpthread   && ./a.out 
stackaddr = (nil)
stacksize = 0
thread join
一共创建了3739个线程
Resource temporarily unavailable
chunli@ubuntu:~/linux_c/thread$



GNU_LIBPTHREAD_VERSION

chunli@ubuntu:~/linux_c/thread$ getconf GNU_LIBPTHREAD_VERSION
NPTL 2.23



细节注意


1.主线程退出其他线程不退出,主线程应调用ptrhed_exit

2.避免僵线程

join

pthread_deatch

pthread_create指定分离属性

被join线程可能在join函数返回前就释放完自己的所有内存资源,所以不应当返回被回收线程栈中的值;

3.malloc和mmap申请的内存可以被其他线程释放

4.如果线程终止时没有释放加锁的互斥量,则该互斥量不能再被使用

5.应避免在多线程模型中调用fork除非,马上exec,子进程中只有调用fork的线程存

在,其他线程在子进程中均pthread_exit

6.信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制




练习


1.测试当前系统允许创建的最大线程个数


2.多线程拷贝命令,如:./my_cp srcfile destfile N(拷贝线程个数)

考察点:

mmap

lseek拓展一个文件,write一个字节,使文件真正拓展

多线程编程模型

线程控制原语


3.多线程检索,改写之前的开房数据查询系统