Linux 系统编程 学习:09-线程:线程的创建、回收与取消

背景

我们在此之前完成了 有关进程的学习。从这一讲开始我们学习线程。

完全的开发可以参考:《多线程编程指南》

Linux 系统编程 学习:有关概念中,我们介绍了线程和进程的概念。

概念

基础概念:

  • 线程是cpu或操作系统调度的基本单位。线程大部分的资源是共享的,仅仅申请了自己的栈、空间。
  • 线程是进程内部的一个执行分支,线程量级很小。
  • 在程序中创建线程,可以提高效率,进程内线程越多,争夺到CPU的概率就越大,执行代码的概率就越大(有一个度)。
  • 线程可以解决很多问题,而不会像进程一样有那么多的开销。
  • 在线程中需要注意同步的问题。一个线程的bug很可能会引起该进程的崩溃。

线程与进程的内存分布不同:

  • 每个进程在创建的时候都申请了新的内存空间以存储代码段\数据段\BSS段\堆\栈空间,并且这些的空间的初始化值是父进程空间的,父子进程在创建后不能互访资源。
  • 而每个新创建的线程则仅仅申请了自己的栈、空间;与同进程的其他线程共享该进程的其他数据空间包括代码段\数据段\BSS段\堆以及打开的库、mmap映射的文件与共享的空间,使得同进程下的线程共享数据十分的方便,只需借助这些共享区域即可,但也有问题即是同步问题。

线程开发基本步骤

在接下来的开发中,我们会介绍有关的函数;

所有线程是在<pthread.h>中,且编译时需要链接pthread库;下面不再说明。

同时,如果没有特殊说明,所有的函数在失败时都返回错误号(error number)。

线程的创建、回收与取消

创建线程

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);

描述:创建一个线程,线程创建成功以后,开始执行指定的函数。

默认情况下,一个线程所使用的内存资源在应用pthread_join调用之前不会被重新分配,所以对于每个线程必须调用一次pthread_join函数(分离线程除外)。

参数解析:

thread:存放线程ID号的对象

attr:创建线程时设置的有关属性,可为空(这里先略过,我们会在后面专门讲到)

start_routine:线程执行的函数入口

arg:执行函数时附带的参数,可为空

返回值:成功返回0,从thread可以获取到线程ID;失败返回错误号。

回收线程

int pthread_join(pthread_t thread, void **retval);

描述:阻塞等待一个线程,并回收其资源。

默认情况下,新创建的线程是joinable的,线程退出后,需对其进行pthread_join操作。

如果不关心线程的返回值,我们可以告诉系统,当线程退出时,自动释放线程资源(后面我们会讲到)

参数解析:

thread:指定等待的线程号

retval:非空时,获取由pthread_exit(void *retval);函数传过来的结果。

返回值:成功返回0,失败返回error number。

提出一个终止线程的请求

int pthread_cancel(pthread_t thread);

描述:一般情况下,线程在其主体函数退出的时候会自动终止,但同时也可以因为接收到另一个线程发来的终止(取消)请求而强制终止。

同一进程的线程间,pthread_cancel向另一线程发终止信号。如何处理Cancel信号则由目标线程自己决定:忽略、立即终止、继续运行至Cancelation-point(取消点),由不同的Cancelation状态(pthread_setcancelstate函数设置状态)决定。

被取消线程可以调用pthread_testcancel,让内核去检测是否需要取消当前线程。被取消的线程退出时总是返回-1(常数值PTHREAD_CANCELED)。

如果在取消功能处处于禁用状态下调用pthread_testcancel(),则该函数不起作用。 请务必仅在线程取消线程操作安全的序列中插入pthread_testcancel()。除通过pthread_testcancel()调用以编程方式建立的取消点意外,pthread标准还指定了几个取消点。测试退出点,就是测试cancel信号

返回值: 发送成功返回0(不意味着thread会终止);失败返回错误号。

一个取消线程的例程
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h> void *thread_fun(void *arg)
{
int i=1;
printf("thread start \n");
while(1)
{
// pthread_testcancel(); 如果没有这句话,那么线程不会结束。
i++;
}
return (void *)0;
} int main(int argc, char* argv)
{
void *ret = NULL;
int iret = 0;
pthread_t tid;
pthread_create(&tid, NULL, thread_fun, NULL);
sleep(1);
pthread_cancel(tid);//取消线程
pthread_join(tid, &ret);
printf("thread 3 exit code %d\n", (int)ret);
return 0;
}

线程取消

线程在收到取消请求(pthread_cancel)后会继续运行,直到到达某个取消点(CancellationPoint)。

取消点:线程检查是否被取消并按照请求进行动作的一个位置。

pthreads标准指定了几个取消点,其中包括:

(1)通过pthread_testcancel调用以编程方式建立线程取消点。

(2)线程等待pthread_cond_waitpthread_cond_timewait()中的特定条件。 (错误的程序设计可能会在取消时导致死锁)

(3)被sigwait()阻塞的函数

(4)一些标准的库调用。通常,这些调用包括线程可基于阻塞的函数。

设置一个线程能否被取消

int pthread_setcancelstate(int state, int *oldstate)

描述:设置一个线程能否被取消

参数解析:

state: 状态

  • PTHREAD_CANCEL_ENABLE(缺省动作,收到信号后设为CANCLED状态)
  • PTHREAD_CANCEL_DISABLE(忽略CANCEL信号继续运行)

old_state: 旧状态容器,如果不为 NULL则存入原来的Cancel状态。

设置本线程取消动作的执行时机

int pthread_setcanceltype(int type, int *oldtype)

描述:设置本线程取消动作的执行时机(仅当Cancel状态为Enable时有效)

参数解析:

type : 取消类型

  • PTHREAD_CANCEL_DEFFERED (默认,收到信号后继续运行至下一个取消点再退出)
  • PTHREAD_CANCEL_ASYCHRONOUS, (立即执行取消动作——退出)

oldtype : 旧状态容器,如果不为NULL则存入运来的取消动作类型值。

手动创建取消点

void pthread_testcancel(void)

描述: 手动创建一个取消点,但线程设置了PTHREAD_CANCEL_ENABLEPTHREAD_CANCEL_DEFFERED 属性,且已经有线程发送了取消本线程的请求时,退出;否则直接返回。

注意:由于此函数在线程内执行,执行的位置就是线程退出的位置,所以在执行此函数以前,线程内部的相关资源申请一定要释放掉,很容易造成内存泄露

总结:

1)线程可以调用pthread_setcancelstate()设置被取消的,或者不能被取消的

2)线程的取消的本质是处理取消信号

3)取消线程可以马上进行的,也可以在取消点才取消 (pthread_setcanceltype()

若是在整个程序退出时,要终止各个线程,应该在成功发送 CANCEL 指令后,使用 pthread_join 函数, 等待指定的线程已经完全退出以后, 再继续执行; 否则,很容易产生 “段错误”。

线程遗嘱

遗嘱机制一般用于释放一些资源,比如释放锁,以免其它的线程永远 也不能获得锁,而造成死锁。

void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute);

登记执行压栈清理函数

void pthread_cleanup_push(void (*routine)(void *), void *arg);

描述:登记执行压栈清理函数的操作。

参数解析:

routine:清理函数

arg:清理函数的有关参数

当以下描述的情况发生时自动调用的函数:

  • 线程调用pthread_exit()函数,而不是直接return.
  • 响应取消请求时,也就是有其它的线程对该线程调用pthread_cancel()函数而到达取消条件时。
  • 本线程调用pthread_cleanup_pop()函数,并且其参数非0。

从栈中删除清理函数的操作

void pthread_cleanup_pop(int execute);

描述:从栈中删除清理函数的操作。

参数解析:

execute:标志位,当其非0时,执行pthread_cleanup_push中登记好的函数;否则,将其出栈,不执行。

例程

2条线程使用互斥锁(后面会讲到)抢占一个资源,当占有锁的其中一个线程被意外中断时之前写好了释放锁的遗嘱,另外一条能够正常拿到锁。

#include <stdio.h>
#include <unistd.h> #include <stdlib.h>
#include <pthread.h>
pthread_mutex_t mutex_x=PTHREAD_MUTEX_INITIALIZER; void clean_handler(void *arg)
{
pthread_mutex_unlock((pthread_mutex_t *)arg); // 防止死锁,所以在这里添加解锁操作
printf("unlocked from clean_handler push by thread_fun1\n");
} void *thread_fun1(void *arg)
{
int i=1;
printf("thread1 start \n");
pthread_cleanup_push(clean_handler, &mutex_x);//提前登记线程被取消后需要处理的事情
pthread_mutex_lock(&mutex_x);
printf("thread_fun1 locked.\n");
while(1)
{
pthread_testcancel(); //如果没有这句话,那么线程不会结束。
i++;
}
pthread_mutex_unlock(&mutex_x);
printf("thread_fun1 unlocked\n");
pthread_cleanup_pop(0);
return (void *)0;
} void *thread_fun2(void *arg)
{
int i=1;
printf("thread2 start \n");
for (i = 0; i < 6; ++i)
{
pthread_mutex_lock(&mutex_x);
printf("thread_fun2 locked.\n");
sleep(1);
pthread_mutex_unlock(&mutex_x);
printf("thread_fun2 unlocked\n");
}
printf("thread2 end \n");
return (void *)0;
} int main(int argc, char* argv)
{
void *ret = NULL;
int iret = 0;
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1, NULL, thread_fun1, NULL);
pthread_create(&tid2, NULL, thread_fun2, NULL);
sleep(2);
pthread_cancel(tid1);//取消线程
pthread_join(tid1, &ret); iret =(int)( (int*)ret);
printf("thread 3 exit code %d\n", iret);
while(1);
return 0;
}

Linux 系统编程 学习:09-线程:线程的创建、回收与取消的更多相关文章

  1. Linux 系统编程 学习:10-线程:线程的属性

    Linux 系统编程 学习:10-线程:线程的属性 背景 上一讲我们介绍了线程的创建,回收与销毁:简单地提到了线程属性.这一讲我们就来具体看看,线程的属性. 概述 #include <pthre ...

  2. Linux 系统编程 学习:11-线程:线程同步

    Linux 系统编程 学习:11-线程:线程同步 背景 上一讲 我们介绍了线程的属性 有关设置.这一讲我们来看线程之间是如何同步的. 额外安装有关的man手册: sudo apt-get instal ...

  3. Linux 系统编程 学习 总结

    背景 整理了Liunx 关于 进程间通信的 很常见的知识. 目录 与 说明 Linux 系统编程 学习:000-有关概念 介绍了有关的基础概念,为以后的学习打下基础. Linux 系统编程 学习:00 ...

  4. Linux 系统编程 学习:00-有关概念

    Linux 系统编程 学习:00-有关概念 背景 系统编程其实就是利用系统中被支持的调度API进行开发的一个过程. 从这一讲开始,我们来介绍有关Linux 系统编程的学习. 知识 在进行Linux系统 ...

  5. Linux 系统编程 学习:01-进程的有关概念 与 创建、回收

    Linux 系统编程 学习:01-进程的有关概念 与 创建.回收 背景 上一讲介绍了有关系统编程的概念.这一讲,我们针对 进程 开展学习. 概念 进程的身份证(PID) 每一个进程都有一个唯一的身份证 ...

  6. Linux 系统编程 学习:02-进程间通信1:Unix IPC(1)管道

    Linux 系统编程 学习:02-进程间通信1:Unix IPC(1)管道 背景 上一讲我们介绍了创建子进程的方式.我们都知道,创建子进程是为了与父进程协作(或者是为了执行新的程序,参考 Linux ...

  7. Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号

    Linux 系统编程 学习:03-进程间通信1:Unix IPC(2)信号 背景 上一讲我们介绍了Unix IPC中的2种管道. 回顾一下上一讲的介绍,IPC的方式通常有: Unix IPC包括:管道 ...

  8. Linux 系统编程 学习:04-进程间通信2:System V IPC(1)

    Linux 系统编程 学习:04-进程间通信2:System V IPC(1) 背景 上一讲 进程间通信:Unix IPC-信号中,我们介绍了Unix IPC中有关信号的概念,以及如何使用. IPC的 ...

  9. Linux 系统编程 学习:05-进程间通信2:System V IPC(2)

    Linux 系统编程 学习:05-进程间通信2:System V IPC(2) 背景 上一讲 进程间通信:System V IPC(1)中,我们介绍了System IPC中有关消息队列.共享内存的概念 ...

随机推荐

  1. react项目创建流程

    react 项目搭建 系统: windows 1.安装 node node 下载地址.一路 next 如果遇到 windows 没有权限安装 msi 文件.打开 cmd,运行msiexec /pack ...

  2. PageRank算法(网页排名)

    Google用它来体现网页的相关性和重要性

  3. 057 01 Android 零基础入门 01 Java基础语法 06 Java一维数组 04 案例:求整型数组的数组元素的元素值累加和

    057 01 Android 零基础入门 01 Java基础语法 06 Java一维数组 04 案例:求整型数组的数组元素的元素值累加和 本文知识点:求整型数组的数组元素的元素值累加和 案例:求整型数 ...

  4. SQLMAP注入Access数据库

    今天偶遇一Access数据库 1.首先尝试是否存在注入点,and1=1,and 1=2,发现返回信息不一样 2.使用sqlmap脱裤,发现时Access数据库,不能提权, 3.那就直接暴库吧,sqlm ...

  5. python中numpy.savetxt 参数

    转载:https://blog.csdn.net/qq_36535820/article/details/99543188 numpy.savetxt 参数 numpy.savetxt(fname,X ...

  6. C++ 中explicit的作用

    转载:https://www.cnblogs.com/diligenceday/p/5781408.html C++ 中explicit的作用   explicit作用: 在C++中,explicit ...

  7. 介绍了ASP。净样板

    下载sample application (or see on Github) 内容 问题介绍什么是ASP.NET样板文件NET Boilerplate不是开始创建空的web应用程序从模板域层 关于名 ...

  8. Docker笔记6:Docker 常见命令及镜像管理

    目  录 一.Docker 常用命令 docker version 命令 docker info 命令 二.Docker 镜像管理 搜索镜像:docker search 镜像名 获取镜像:docker ...

  9. dockerfile-maven-plugin极简教程

    目录 一.简介 二.概述 三.将spring-boot-app打包成docker镜像 创建示例应用 修改pom文件 增加Dockerfile文件 使用Maven打包应用 运行应用镜像 四.分析mvn ...

  10. C# 主界面的扁平化

    如果需要查看更多文章,请微信搜索公众号 csharp编程大全,需要进C#交流群群请加微信z438679770,备注进群, 我邀请你进群! ! ! --------------------------- ...