- 使用
malloc
函数申请内存空间 , 这部分空间主要在堆当中 。 - 使用
mmap
系统调用在共享库的映射区申请内存空间 。
#include <stdio.h>#include <pthread.h>#include <stdlib.h>#define MiB * 1 << 20int times = 0;staticvoid* stack_overflow(void* args) {printf("times = %d\n", ++times);char s[1 << 20]; // 1 MiBstack_overflow(NULL);return NULL;}int main() {pthread_attr_t attr;pthread_attr_init(&attr);void* stack = malloc(2 MiB); // 使用 malloc 函数申请内存空间 申请的空间大小为 2 MiBpthread_t t;pthread_attr_setstack(&attr, stack, 2 MiB); // 使用属性设置函数设置栈的位置 栈的最低地址为 stack 栈的大小等于 2 MiBpthread_create(&t, &attr, stack_overflow, NULL);pthread_join(t, NULL);pthread_attr_destroy(&attr); // 释放系统资源free(stack); // 释放堆空间return 0;}
上述程序的执行结果如下图所示:![一 Pthread 并发编程——深入剖析线程基本元素和状态](http://img.zhejianglong.com/231019/10304W3b-9.png)
文章插图
从上面的执行结果可以看出来我们设置的栈空间的大小为 2MB 成功了 。在上面的程序当中我们主要使用
pthread_attr_setstack
函数设置栈的低地址和栈空间的大小 。我们申请的内存空间内存布局大致如下图所示:![一 Pthread 并发编程——深入剖析线程基本元素和状态](http://img.zhejianglong.com/231019/10304VS9-10.png)
文章插图
使用 mmap 申请内存作为栈空间
#define _GNU_SOURCE#include <stdio.h>#include <pthread.h>#include <stdlib.h>#include <sys/mman.h>#define MiB * 1 << 20#define STACK_SIZE 2 MiBint times = 0;staticvoid* stack_overflow(void* args) {printf("times = %d\n", ++times);char s[1 << 20]; // 1 MiBstack_overflow(NULL);return NULL;}int main() {pthread_attr_t attr;pthread_attr_init(&attr);void* stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);if (stack == MAP_FAILED)perror("mapped error:");pthread_t t;pthread_attr_setstack(&attr, stack, STACK_SIZE);pthread_create(&t, &attr, stack_overflow, NULL);pthread_join(t, NULL);pthread_attr_destroy(&attr);free(stack);return 0;}
在上面的程序当中我们使用 mmap 系统调用在共享库空间申请了一段内存空间,并且将其做为栈空间,我们在这里就不将程序执行的结果放出来了,上面整个程序和前面的程序相差不大,只是在申请内存方面发生了变化,总体的方向是不变的 。根据前面知识的学习,我们可以知道多个线程可以共享同一个进程虚拟地址空间,我们只需要给每个线程申请一个栈空间让线程执行起来就行 , 基于此我们可以知道多个线程的执行流和大致的内存布局如下图所示:
![一 Pthread 并发编程——深入剖析线程基本元素和状态](http://img.zhejianglong.com/231019/10304Sb6-11.png)
文章插图
在上图当中不同的线程拥有不同的栈空间和每个线程自己的寄存器现场,正如上图所示,栈空间可以是在堆区也可以是在共享库的映射区域,只需要给线程提供栈空间即可 。
深入理解线程的状态在
pthread
当中给我们提供了一个函数 pthread_cancel
可以取消一个正在执行的线程,取消正在执行的线程之后会将线程的退出状态(返回值)设置成宏定义 PTHREAD_CANCELED
。我们使用下面的例子去理解一下线程取消的过程:#include <stdio.h>#include <pthread.h>#include <assert.h>void* task(void* arg) { while(1) {pthread_testcancel(); // 测试是否被取消执行了}return NULL;}int main() {void* res;pthread_t t;pthread_create(&t, NULL, task, NULL);int s = pthread_cancel(t); // 取消函数的执行if(s != 0)fprintf(stderr, "cancel failed\n");pthread_join(t, &res);assert(res == PTHREAD_CANCELED);return 0;}
在上面的程序当中我们在主线程当中使用函数 pthread_cancel
函数取消线程的执行,编译执行上面的程序是可以通过的,也就是说程序正确执行了 , 而且 assert 也通过了 。我们先不仔细去分析上面的代码的执行流和函数的意义 。我们先需要了解一个线程的基本特性 。与线程取消执行相关的一共有两个属性,分别是:
- 取消执行的状态,线程的取消执行的状态一共有两个:
- PTHREAD_CANCEL_ENABLE:这个状态表示这个线程是可以取消的,也是线程创建时候的默认状态 。
- PTHREAD_CANCEL_DISABLE:这个状态表示线程是不能够取消的 , 如果有一个线程发送了一个取消请求,那么这个发送取消消息的线程将会被阻塞直到线程的取消状态变成 PTHREAD_CANCEL_ENABLE。
- 取消执行的类型 , 取消线程执行的类型也有两种:
推荐阅读
- hush韩剧结局_hush韩剧最后一集的剧情
- 苹果xr参数配置_苹果xr参数配置详细参数
- FHQ Treap 详解
- 一 UBOOT编译--- make xxx_deconfig过程详解
- Ubuntu安装Docker及镜像加速器
- HYTHIOL-C PLUS白兔牌美白丸 吃了一个月后的反馈
- 十一 前后端分离项目:实现"删"功能(前后端)
- 七 pod:静态pod
- 夯实Java基础,一篇文章全解析线程问题
- 天猫童装店铺销量排行榜前十,巴拉巴拉童装销量第一