【本文谢绝转载原文来自http://990487026.blog.51cto.com】
<大纲>Linux系统开发8 线程
线程概念
浏览器 火狐多线程,谷歌多进程比较:
查看某一个进程有哪些线程
线程间共享资源
线程间非共享资源
线程优缺点
安装完整的manpage文档
pthread_create()创建线程
pthread_self() 获取线程自己的ID
线程创建程序演示:
指定libpthread.so库编译链接
演示:进程结束,线程也会立即结束
pthread_exit() 调用线程退出函数
pthread_join()回收线程的资源
pthread_cancel() 在进程内某个线程可以终止另一个线程,依然是join()回收线程资源
pthread_detach()分离线程tid,资源由系统自动管理,不能使用join()回收
pthread_join() pthread_exit() pthread_cancel() 线程程序演示
pthread_join()一个线程不能回收两次
把线程转成分离态,分离态的线程不能pthread_join()
线程终止的几种方式
线程pthread_cancel()终止方式,没有执行系统调用不会立即终止线程
线程pthread_cancel()终止方式,执行系统调用会立即终止线程
查看linux系统栈大小
测试最大线程数
创建一个线程,变成分状态的线程,僵尸线性
设置线程属性,线程栈
设置线程属性,为线程分配占空间,批量创建
LIBPTHREAD_VERSION查看
细节注意
练习
线程概念
什么是线程,线程和进程的关系
1.轻量级进程(light-weight process),也有PCB,创建线程使用的底层函数和进程一样,都是clone
2.从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存资源的三级页表是相同的
3.进程可以蜕变成线程
4.在美国人眼里,线程就是寄存器和栈
5.在linux下,线程最是小的执行单位;进程是最小的分配资源单位
浏览器 火狐多线程,谷歌多进程比较:
火狐浏览器,打来3个标签页,浏览网页内容:
老师说火狐是多线程,可以看出来都是同一个PID 16778,多个LWP
chunli@ubuntu:~$ ps -eLf | head -n 1; ps -eLf |grep 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 <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.多线程检索,改写之前的开房数据查询系统
本文出自 “李春利” 博客,谢绝转载!