Linux系统开发8 线程

时间:2022-10-21 18:52:17


本文谢绝转载原文来自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下,线程最是小的执行单位;进程是最小的分配资源单位

Linux系统开发8 线程





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

火狐浏览器,打来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.调度优先级


Linux系统开发8 线程


线程优缺点


【优点】

提高程序的并发性

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

通信和共享数据方便


【缺点】

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

线程调试比较困难(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 <stdio.h>#include <pthread.h>#include <sys/types.h>#include <unistd.h>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 <pthread.h>//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 <stdio.h>#include <pthread.h>#include <sys/types.h>#include <unistd.h>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 <pthread.h>//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 <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>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 <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <string.h>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));elsefprintf(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 <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <string.h>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));elsefprintf(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 <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <string.h>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 <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <string.h>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 <stdio.h>#include <stdlib.h>#include <string.h>#include <pthread.h>#include <unistd.h>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 <stdio.h>#include <string.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>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 valuespthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//Threads that are created using attr will be created in a detached statepthread_create(&tid,&attr,th_fun,NULL);// starts a new thread in the calling processerr = pthread_join(tid,NULL); //waits for the thread specified by thread to terminatewhile(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] <defunct>或者:另外一个窗口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] <defunct>    可以看到变成了僵尸线程



设置线程属性,线程栈

chunli@ubuntu:~/linux_c/thread$ cat pthread_attr_setstack.c #include <stdio.h>#include <string.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>void *th_fun(void * argc){int i = 10;while(i--){printf("线程开始干活 %d\n",i);sleep(1);}}int  main(){pthread_ttid;pthread_attr_tattr;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 objectprintf("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 detachstateif(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 <stdio.h>#include <string.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>void *th_fun(void * argc){int i = 10;while(i--){//printf("线程开始干活 %d\n",i);sleep(1);}}int  main(){pthread_ttid;pthread_attr_tattr;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 objectprintf("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 detachstateif(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.多线程检索,改写之前的开房数据查询系统









本文出自 “李春利” 博客,谢绝转载!