本文谢绝转载原文来自

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

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

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

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

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

1,比如我想看会话信息chunli@ubuntu:~$ ps -eLf | grep session-childUID         PID   PPID    LWP  C NLWP STIME TTY          TIME CMDroot      16952   3759  16952  0    3 09:57 ?        00:00:00 lightdm --session-child 16 19root      16952   3759  16953  0    3 09:57 ?        00:00:00 lightdm --session-child 16 19root      16952   3759  16954  0    3 09:57 ?        00:00:00 lightdm --session-child 16 19root      17018   3759  17018  0    1 09:57 ?        00:00:00 lightdm --session-child 12 19chunli    17178   4619  17178  0    1 10:01 pts/8    00:00:00 grep --color=auto session-childchunli@ubuntu:~$ 2,指定会话的PIDchunli@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 19chunli@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-devmanpages 包含 GNU/Linux 的基本操作manpages-dev 包含 GNU/Linux 的基本操作APImanpages-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.h>

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.0chunli@ubuntu:~/linux_c/thread$ gcc pthread.c  -lpthread && ./a.out main thrdad ID = 140620739671808main child thrdad ID = 140620731340544main PID = 24697thread PID = 24697thread ID = 140620731340544thread n = 10chunli@ubuntu:~$ ps -eLf |head -n 1 ;  ps -eLf | grep a.outUID         PID   PPID    LWP  C NLWP STIME TTY          TIME CMDchunli    24655   6016  24655  0    2 11:46 pts/9    00:00:00 ./a.outchunli    24655   6016  24656  0    2 11:46 pts/9    00:00:00 ./a.outchunli    24683  17152  24683  0    1 11:47 pts/12   00:00:00 grep --color=auto a.outchunli@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.outchunli@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 = 139956336580352main child thrdad ID = 139956328249088main PID = 24716chunli@ubuntu:~/linux_c/thread$ 线程的printf("Hello Linux!\n");的这句话没有来得及执行

pthread_exit() 调用线程退出函数

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

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

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

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

数已经退出了。

#include <pthread.h>

void pthread_exit(void *retval);

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

pthread_join()回收线程的资源

#include <pthread.h>

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 <pthread.h>

int pthread_cancel(pthread_t thread);

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

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

#define PTHREAD_CANCELED ((void *) -1)

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

#include <pthread.h>

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 returningthread 1 exit code 1thread 2 exitingthread 2 exit code 2thread 3 waitingthread 3 waitingthread 3 waitingthread 3 exit code -1chunli@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 2thread count 1thread count 0thread exit code 1thread No such processthread No such processthread No such process^Cchunli@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 argumentthread count 2thread Invalid argumentthread count 1thread count 0thread Invalid argumentthread Invalid argumentthread Invalid argumentthread Invalid argumentthread Invalid argumentthread Invalid argument^Cchunli@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.outUID         PID   PPID    LWP  C NLWP STIME TTY          TIME CMDchunli    25330   6016  25330  0    2  4213  1304   1 16:01 pts/9    00:00:00 ./a.outchunli    25330   6016  25331 99    2  4213  1304   3 16:01 pts/9    00:00:16 ./a.outchunli@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.outUID         PID   PPID    LWP  C NLWP STIME TTY          TIME CMDchunli    25342   6016  25342  0    1  4213  1616   3 16:03 pts/9    00:00:00 ./a.outchunli@ubuntu:~$

查看linux系统栈大小

chunli@ubuntu:~/linux_c/thread$ ulimit -acore file size          (blocks, -c) 0data seg size           (kbytes, -d) unlimitedscheduling priority             (-e) 0file size               (blocks, -f) unlimitedpending signals                 (-i) 3749max locked memory       (kbytes, -l) 64max memory size         (kbytes, -m) unlimitedopen files                      (-n) 1024pipe size            (512 bytes, -p) 8POSIX message queues     (bytes, -q) 819200real-time priority              (-r) 0stack size              (kbytes, -s) 8192cpu time               (seconds, -t) unlimitedmax user processes              (-u) 3749virtual memory          (kbytes, -v) unlimitedfile locks                      (-x) unlimitedchunli@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 最大线程数3736Resource temporarily unavailablechunli@ubuntu:~/linux_c/thread$

系统最大线程数

chunli@ubuntu:~/linux_c/thread$ cat /proc/sys/kernel/threads-max7498chunli@ubuntu:~/linux_c/thread$ chunli@ubuntu:~/linux_c/thread$ ulimit -s 8192chunli@ubuntu:~/linux_c/thread$ ulimit -s 4096chunli@ubuntu:~/linux_c/thread$ ulimit -s 4096Ubuntu16.04 X64 在命令行调整栈大小,没有用老师在32bit下缩小ulimit -s的数值,线程量多一倍chunli@ubuntu:~/linux_c/thread$ gcc stack.c  -lpthread -Wall  && ./a.out 最大线程数3736Resource temporarily unavailablechunli@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 argument7f40f931e700  97f40f931e700  87f40f931e700  77f40f931e700  67f40f931e700  57f40f931e700  47f40f931e700  37f40f931e700  27f40f931e700  17f40f931e700  0chunli@ubuntu:~/linux_c/thread$ 另外一个窗口ps aux观测chunli@ubuntu:~$ ps aux > /tmp/ps;head -n 1 /tmp/ps;grep a.out < /tmp/psUSER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMANDchunli     5233  0.0  0.0  14716   760 pts/9    Sl+  20:58   0:00 ./a.outchunli@ubuntu:~$ ps aux > /tmp/ps;head -n 1 /tmp/ps;grep a.out < /tmp/psUSER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMANDchunli     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 = 0thread join线程开始干活 9线程开始干活 8线程开始干活 7线程开始干活 6线程开始干活 5线程开始干活 4线程开始干活 3线程开始干活 2线程开始干活 1线程开始干活 0回收线程OKchunli@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 = 0thread join一共创建了3739个线程Resource temporarily unavailablechunli@ubuntu:~/linux_c/thread$

GNU_LIBPTHREAD_VERSION

chunli@ubuntu:~/linux_c/thread$ getconf GNU_LIBPTHREAD_VERSIONNPTL 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.多线程检索,改写之前的开房数据查询系统