【本文谢绝转载原文来自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
[email protected]:~$ 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 [email protected]:~$
谷歌浏览器,打来3个标签页,浏览网页内容:
老师说火狐是多进程,可以看出来都是同多个PID ,多个LWP
[email protected]:~$ 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 [email protected]:~$
查看某一个进程有哪些线程:
1,比如我想看会话信息 [email protected]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 [email protected]:~$ 2,指定会话的PID [email protected]:~$ 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 [email protected]:~$
线程间共享资源
1.文件描述符表
2.每种信号的处理方式
3.当前工作目录
4.用户ID和组ID
5.内存地址空间 Text data bss 堆 共享库
线程间非共享资源
1.线程id
2.处理器现场和栈指针(内核栈)
3.独立的栈空间(用户空间栈)
4.errno变量
5.信号屏蔽字
6.调度优先级
线程优缺点
【优点】
提高程序的并发性
开销小,不用重新分配内存
通信和共享数据方便
【缺点】
线程不稳定(库函数实现)
线程调试比较困难(gdb支持不好)
线程无法使用unix经典事件,例如信号
安装完整的manpage文档
[email protected]:~$ sudo apt-get install -y manpages manpages-dev [email protected]:~$ 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.h>
pthread_t pthread_self(void);
由于pthread_create的错误码不保存在errno中,因此不能直接用perror(3)打印错误信
息,可以先用strerror(3)把错误码转换成错误信息再打印。
如果任意一个线程调用了exit或_exit,则整个进程的所有线程都终止,由于从main函
数return也相当于调用exit,为了防止新创建的线程还没有得到执行就终止,我们在main函
数return之前延时1秒,这只是一种权宜之计,即使主线程等待1秒,内核也不一定会调度新
创建的线程执行,下一节我们会看到更好的办法。
线程创建程序演示:
[email protected]:~/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库编译链接
[email protected]:~$ 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 [email protected]:~/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 [email protected]:~$ 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 [email protected]:~$ 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 [email protected]:~$
演示:进程结束,线程也会立即结束
[email protected]:~/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; } [email protected]:~/linux_c/thread$ gcc pthread.c -lpthread && ./a.out main thrdad ID = 139956336580352 main child thrdad ID = 139956328249088 main PID = 24716 [email protected]:~/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() 线程程序演示
[email protected]:~/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的扩展。 [email protected]:~/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 [email protected]:~/linux_c/thread$
pthread_join()一个线程不能回收两次
[email protected]:~/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)); else fprintf(stderr, "thread exit code %zd\n", (size_t)tret); sleep(1); } return 0; } [email protected]:~/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 [email protected]:~/linux_c/thread$
把线程转成分离态,分离态的线程不能pthread_join()
[email protected]:~/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)); else fprintf(stderr, "thread exit code %zd\n", (size_t)tret); sleep(1); } return 0; } [email protected]:~/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 [email protected]:~/linux_c/thread$
线程终止的几种方式
如果需要只终止某个线程而不终止整个进程,可以有三种方法:
1.从线程主函数return。这种方法对主控线程不适用,从main函数return相当于调用exit。
2.一个线程可以调用pthread_cancel终止同一进程中的另一个线程。
3.线程可以调用pthread_exit终止自己。
同一进程的线程间,pthread_cancel向另一线程发终止信号。
系统并不会马上关闭被取消线程,只有在被取消线程下次系统调用时,
才会真正结束线程。或调用pthread_testcancel,让内核去检测是否需要取消当前线程
线程pthread_cancel()终止方式,没有执行系统调用不会立即终止线程
[email protected]:~/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; } [email protected]:~/linux_c/thread$ gcc pthread_detach.c -lpthread -Wall && ./a.out 线程并没有终止,因为线程没有做任何系统调用 [email protected]:~$ 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 [email protected]:~$
线程pthread_cancel()终止方式,执行系统调用会立即终止线程
[email protected]:~/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; } [email protected]:~/linux_c/thread$ gcc pthread_detach.c -lpthread -Wall && ./a.out 线程会被终止,因为线程做了系统调用 [email protected]:~$ 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 [email protected]:~$
查看linux系统栈大小
[email protected]:~/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 [email protected]:~/linux_c/thread$
测试最大线程数
[email protected]:~/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; } [email protected]:~/linux_c/thread$ gcc stack.c -lpthread -Wall && ./a.out 最大线程数3736 Resource temporarily unavailable [email protected]:~/linux_c/thread$
系统最大线程数
[email protected]:~/linux_c/thread$ cat /proc/sys/kernel/threads-max 7498 [email protected]:~/linux_c/thread$ [email protected]:~/linux_c/thread$ ulimit -s 8192 [email protected]:~/linux_c/thread$ ulimit -s 4096 [email protected]:~/linux_c/thread$ ulimit -s 4096 Ubuntu16.04 X64 在命令行调整栈大小,没有用 老师在32bit下缩小ulimit -s的数值,线程量多一倍 [email protected]:~/linux_c/thread$ gcc stack.c -lpthread -Wall && ./a.out 最大线程数3736 Resource temporarily unavailable [email protected]:~/linux_c/thread$
创建一个线程,变成分状态的线程,僵尸线性
[email protected]:~/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 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; } 编译运行: [email protected]:~/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 [email protected]:~/linux_c/thread$ 另外一个窗口ps aux观测 [email protected]:~$ 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 [email protected]:~$ 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] <defunct> 或者:另外一个窗口ps ajx观测 [email protected]:~$ 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 [email protected]:~$ 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> 可以看到变成了僵尸线程
设置线程属性,线程栈
[email protected]:~/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_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; } [email protected]:~/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 [email protected]:~/linux_c/thread$
设置线程属性,为线程分配占空间,批量创建
[email protected]:~/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_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; } [email protected]:~/linux_c/thread$ gcc pthread_attr_setstack.c -lpthread && ./a.out stackaddr = (nil) stacksize = 0 thread join 一共创建了3739个线程 Resource temporarily unavailable [email protected]:~/linux_c/thread$
GNU_LIBPTHREAD_VERSION
[email protected]:~/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.多线程检索,改写之前的开房数据查询系统