1 线程基本知识

\quad进程是资源管理的基本单元,而线程是系统调度的基本单元,线程是操作系统能够进行调度运算的最小单位,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

\quad一个进程在某一个时刻只能做一件事情,有了多个控制线程以后,在程序的设计成在某一个时刻能够做不止一件事,每个线程处理独自的任务。

\quad需要注意的是:即使程序运行在单核处理器上,也能够得到多线程编程模型的好处。处理器的数量并不影响程序结构,所以不管处理器个数多少,程序都可以通过线程得以简化。

\quadlinux操作系统使用符合POSIX线程作为系统标准线程,该POSIX线程标准定义了一整套操作线程的API。

2. 线程标识

\quad与进程有一个ID一样,每个线程有一个线程ID,所不同的是,进程ID在整个系统中是唯一的,而线程是依附于进程的,其线程ID只有在所属的进程中才有意义。线程ID用pthread_t表示。

//pthread_self直接返回调用线程的ID
include <pthread.h>
pthread_t pthread_self(void);

\quad判断两个线程ID的大小是没有任何意义的,但有时可能需要判断两个给定的线程ID是否相等,使用以下接口:

//pthread_equal如果t1和t2所指定的线程ID相同,返回0;否则返回非0值。
include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);

3. 线程创建

一个线程的生命周期起始于它被创建的那一刻,创建线程的接口:

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

函数参数:

thread(输出参数),由pthread_create在线程创建成功后返回的线程句柄,该句柄在后续操作线程的API中用于标志该新建的线程;
start_routine(输入参数),新建线程的入口函数;
arg(输入参数),传递给新线程入口函数的参数;
attr(输入参数),指定新建线程的属性,如线程栈大小等;如果值为NULL,表示使用系统默认属性。

函数返回值:

成功,返回0;
失败,返回相关错误码。

需要注意:

  1. 主线程,这是一个进程的初始线程,其入口函数为main函数。
  2. 新线程的运行时机,一个线程被创建之后有可能不会被马上执行,甚至,在创建它的线程结束后还没被执行;也有可能新线程在当前线程从pthread_create前就已经在运行,甚至,在pthread_create前从当前线程返回前新线程就已经执行完毕。

程序实例:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>void printids(const char *s){pid_t pid;pthread_t tid;pid = getpid();tid = pthread_self();printf("%s, pid %lu tid %lu (0x%lx)\n",s,(unsigned long)pid,(unsigned long)tid,(unsigned long)tid);
}void *thread_func(void *arg){printids("new thread: ");return ((void*)0);
}
int main() {int err;pthread_t tid;err = pthread_create(&tid,NULL,thread_func,NULL);if (err != 0) {fprintf(stderr,"create thread fail.\n");exit(-1); }printids("main thread:");sleep(1);   return 0;
}

注意上述的程序中,主线程休眠一秒,如果不休眠,则主线程不休眠,则其可能会退出,这样新线程可能不会被运行,我自己注释掉sleep函数,发现好多次才能让新线程输出。

编译命令:

gcc -o thread thread.c -lpthread

运行结果如下:

main thread:, pid 889 tid 139846854309696 (0x7f30a212f740)
new thread: , pid 889 tid 139846845961984 (0x7f30a1939700)

可以看到两个线程的进程ID是相同的。其共享进程中的资源。

4. 线程终止

线程的终止分两种形式:被动终止和主动终止

被动终止有两种方式:

  1. 线程所在进程终止,任意线程执行exit_Exit或者_exit函数,都会导致进程终止,从而导致依附于该进程的所有线程终止。
  2. 其他线程调用pthread_cancel请求取消该线程。

主动终止也有两种方式:

  1. 在线程的入口函数中执行return语句,main函数(主线程入口函数)执行return语句会导致进程终止,从而导致依附于该进程的所有线程终止。
  2. 线程调用pthread_exit函数,main函数(主线程入口函数)调用pthread_exit函数, 主线程终止,但如果该进程内还有其他线程存在,进程会继续存在,进程内其他线程继续运行。

线程终止函数:

include <pthread.h>
void pthread_exit(void *retval);

\quad线程调用pthread_exit函数会导致该调用线程终止,并且返回由retval指定的内容。
\quad注意:retval不能指向该线程的栈空间,否则可能成为野指针!

5. 管理线程的终止

5.1 线程的连接

\quad一个线程的终止对于另外一个线程而言是一种异步的事件,有时我们想等待某个ID的线程终止了再去执行某些操作,pthread_join函数为我们提供了这种功能,该功能称为线程的连接:

include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

参数说明:

thread(输入参数),指定我们希望等待的线程
retval(输出参数),我们等待的线程终止时的返回值,就是在线程入口函数中return的值或者调用pthread_exit函数的参数

返回值:

成功时,返回0
错误时,返回正数错误码

\quad当线程X连接线程Y时,如果线程Y仍在运行,则线程X会阻塞直到线程Y终止;如果线程Y在被连接之前已经终止了,那么线程X的连接调用会立即返回。

\quad连接线程其实还有另外一层意义,一个线程终止后,如果没有人对它进行连接,那么该终止线程占用的资源,系统将无法回收,而该终止线程也会成为僵尸线程。因此,当我们去连接某个线程时,其实也是在告诉系统该终止线程的资源可以回收了。

\quad注意:对于一个已经被连接过的线程再次执行连接操作, 将会导致无法预知的行为!

5.2 线程的分离

\quad有时我们并不在乎某个线程是不是已经终止了,我们只是希望如果某个线程终止了,系统能自动回收掉该终止线程所占用的资源。pthread_detach函数为我们提供了这个功能,该功能称为线程的分离:

#include <pthread.h>
int pthread_detach(pthread_t thread);

\quad默认情况下,一个线程终止了,是需要在被连接后系统才能回收其占有的资源的。如果我们调用pthread_detach函数去分离某个线程,那么该线程终止后系统将自动回收其资源。

/*
* 文件名: thread_sample1.c
* 描述:演示线程基本操作
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>/*子线程1入口函数*/
void *thread_routine1(void *arg)
{fprintf(stdout, "thread1: hello world!\n");sleep(1);/*子线程1在此退出*/return NULL;
}/*子线程2入口函数*/
void *thread_routine2(void *arg)
{fprintf(stdout, "thread2: I'm running...\n");pthread_t main_thread = (pthread_t)arg;/*分离自我,不能再被连接*/pthread_detach(pthread_self());/*判断主线程ID与子线程2ID是否相等*/if (!pthread_equal(main_thread, pthread_self())) {fprintf(stdout, "thread2: main thread id is not equal thread2\n");}/*等待主线程终止*/pthread_join(main_thread, NULL);fprintf(stdout, "thread2: main thread exit!\n");fprintf(stdout, "thread2: exit!\n");fprintf(stdout, "thread2: process exit!\n");/*子线程2在此终止,进程退出*/pthread_exit(NULL);
}int main(int argc, char *argv[])
{/*创建子线程1*/pthread_t t1;if (pthread_create(&t1, NULL, thread_routine1, NULL)!=0) {fprintf(stderr, "create thread fail.\n");exit(-1);}/*等待子线程1终止*/pthread_join(t1, NULL);fprintf(stdout, "main thread: thread1 terminated!\n\n");/*创建子线程2,并将主线程ID传递给子线程2*/pthread_t t2;if (pthread_create(&t2, NULL, thread_routine2, (void *)pthread_self())!=0) {fprintf(stderr, "create thread fail.\n");exit(-1);}fprintf(stdout, "main thread: sleeping...\n");sleep(3);/*主线程使用pthread_exit函数终止,进程继续存在*/fprintf(stdout, "main thread: exit!\n");pthread_exit(NULL);    fprintf(stdout, "main thread: never reach here!\n");return 0;
}

最终的执行结果如下:

thread1: hello world!
main thread: thread1 terminated!main thread: sleeping...
thread2: I'm running...
thread2: main thread id is not equal thread2
main thread: exit!
thread2: main thread exit!
thread2: exit!
thread2: process exit!

参考资料:

  1. https://www.shiyanlou.com/courses/731
  2. 《unix环境高级编程》

Linux多线程编程入门相关推荐

  1. Linux网络编程 入门

    Linux网络编程入门 (转载) (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍 客户端和服务端          网络程序和普通的程序有一个最大的区别是网络程序是由两个 ...

  2. 一文带你Linux系统编程入门

    文件和文件系统 文件是linux系统中最重要的抽象,大多数情况下你可以把linux系统中的任何东西都理解为文件,很多的交互操作其实都是通过文件的读写来实现的. 文件描述符 在linux内核中,文件是用 ...

  3. linux多线程编程和linux 2.6下的nptl,Linux多线程编程和Linux 2.6下的NPTL

    这几天由于工作需要,琢磨了一下Linux下的多线程的相关资料.Linux下最常用的多线程支持库为Pthread库,它是glibc库的组成部分.但是关于Pthread的说明文档非常缺乏,特别是对POSI ...

  4. ZT 为什么pthread_cond_t要和pthread_mutex_t同时使用 || pthread/Linux多线程编程

    为什么线程同步的时候pthread_cond_t要和pthread_mutex_t同时使用 (2009-10-27 11:07:23) 转载▼ 标签: 杂谈 分类: 计算机 举一个例子(http:// ...

  5. Linux动态链接库编程入门

    Linux动态链接库编程入门 转:http://blog.csdn.net/yang_rong_yong/article/details/3090212 动态链接库是一种通用的软件组件技术,是多种操作 ...

  6. 【学习笔记】Linux 系统编程入门

    Linux 系统编程入门 静态库与动态库 静态库命名规则 静态库的制作 静态库使用 动态库制作 动态库使用 加载动态库 静态库的优缺点 动态库的优缺点 Makefile 文件命名 工作原理 变量 模式 ...

  7. linux查询某域线程是否满了,Linux多线程编程的时候怎么查看一个进程中的某个线程是否存活...

    pthread_kill: 别被名字吓到,pthread_kill可不是kill,而是向线程发送signal.还记得signal吗,大部分signal的默认动作是终止进程的运行,所以,我们才要用sig ...

  8. linux线程 ppt,Linux多线程编程多核编程.ppt

    <Linux多线程编程多核编程.ppt>由会员分享,可在线阅读,更多相关<Linux多线程编程多核编程.ppt(28页珍藏版)>请在装配图网上搜索. 1.Linux多线程编程, ...

  9. 多核程序设计 linux,多核程序设计Linux多线程编程.ppt

    Linux多线程编程,IEEE POSIX 标准 p1003.1c (Pthreads) 定义了处理线程的一系列C 语言类型的API. 在Linux中,线程一般被认为是"轻量级的进程&quo ...

  10. Linux 多线程编程(实现生产者消费者模型)

    Linux 多线程编程 线程分类 线程按照其调度者可以分为用户级线程和内核级线程两种. 内核级线程 在一个系统上实现线程模型的方式有好几种,因内核和用户空间提供的支持而有一定程度的级别差异.最简单的模 ...

最新文章

  1. uniapph5配置index.html模板路径不生效解决办法
  2. eclipse配置struts.xml自动提示
  3. Linux命令学习之nslookup
  4. LaTeX中警告类型及说明
  5. alv导出本地文件DUMP
  6. 深度学习笔记之DenseNets
  7. 制作点击文字变颜色_腾讯的动态时间轴PPT火了!制作简单又有逼格,都学起来啊...
  8. 分布式队列编程:模型、实战
  9. 嵌入式基础认识1:存储器(如RAM、ROM和FLASH)
  10. 一至七-----小东西
  11. Linux用户管理命令详解,useradd、passwd
  12. socket.io简介
  13. oracle跨库连接查询
  14. UVa 10499 - The Land of Justice
  15. Python编写杨辉三角形
  16. 百度地图 根据经纬度 定位
  17. Promise中then的执行顺序详解
  18. 106短信平台备受欢迎的原因
  19. IT能力框架(模型)
  20. 2022年系统集成项目管理工程师考试,需要知道这些

热门文章

  1. 银行卡号返回银行信息
  2. modelsim编译c语言,Modelsim协同SystemC仿真
  3. Visio 2003 开发入门
  4. android屏幕同步到macbook,Mirror for Android TV for Mac(屏幕和声音镜像到Android TV的工具)...
  5. 关于修改DSDT出现的常见问题
  6. 西门子1212c 通过高速脉冲输出控制台达B2伺服电机
  7. ORACLE日期时间函数大全
  8. 矩阵的逆和矩阵的转置运算公式对比
  9. 大学生创新创业 /互联网+ 大赛 商业计划书目录(模板)
  10. 卸载不了mysql2008_卸载SQL2008遇到的问题及解决办法