《UNIX环境高级编程》笔记53--线程私有数据

线程私有数据时存储和查询与某个线程相关的数据的一种机制。把这种数据成为线程私有数据或线程特定数据的原因是,

希望每个线程都可以独立地访问数据副本,并不需要担心与其他线程的同步访问问题。

为什么要使用线程私有数据呢?原因有两个:

1.有时候需要维护基于每个线程的数据,因为线程ID并不能保证是小而连续的整数,所以不能简单地分配一个线程数据

数组,用线程ID作为数组的索引。即使线程ID确实是小而连续的整数,可能还希望有一些额外的保护,以防止某个线程的数

据与其他线程数据相混淆。

2.它提供了让基于进程的接口适应多线程环境的机制。一个很明显的实例就是errno,在多线程环境下,errno不能简单地

定义为一个全局变量,因为多个线程可能使用在读写该变量。为了让线程能够使用那些原本基于进程的线程调用和库例程,

errno被重新定义为线程私有数据。


在分配线程私有数据之前,需要创建于该数据关联的键,这个键将用于获取对线程私有数据的访问权,使用

pthread_key_create创建一个键。

[cpp]  view plain  copy
  1. #include<pthread.h>  
  2. int pthread_key_create(pthread_key_t *keyp, void (*destructor) (void*));  
  3. //成功返回0,否则返回错误编号。  

创建的键存放在keyp指向的内存单元,这个键额可以被进程中的所有线程使用,但每个线程把这个键与不同的线程私有数据

地址进行关联。创建新键时,每个线程的数据地址设为null值。

除了创建键以外,pthread_key_create可以选择为该键关联析构函数,当线程退出时,如果数据地址已经被设置为非null,那

么析构函数就会被调用,它唯一的参数就是该数据地址。如果传入的destructor参数为null,就表明没有析构函数与键关联。

当线程调用pthread_exit或者线程执行返回,正常退出时,析构函数就会被调用,但是如果线程调用了exit、_exit、_Exit、abort

或者出现其他非正常的退出时,就不会调用析构函数。

线程通常使用malloc为线程私有数据分配内存空间,析构函数通常释放以分配的内存,如果线程没有释放内存就退出了,那么

这块内存就会泄露。

如果多个线程都调用了pthread_key_create函数, 那么有些线程可能看到某个键值,而其他的线程看到的可能是另一个不

同的线程,解决这种竞争的方法是使用pthread_once.

[cpp]  view plain  copy
  1. #include<pthread.h>  
  2. pthread_once_t initflag = PTHREAD_ONCE_INIT;  
  3. int pthread_once(pthread_once_t *initflag, void(*initfn)(void));  
  4. //成功则返回0,否则返回错误编号。  

initflag必须是一个非本地变量(全局变量或静态变量),必须初始化为PTHREAD_ONCE_INIT.如果每个线程都调用

pthread_once,系统就能保证初始化例程initfn只被调用一次。


对所有的线程,都可以通过调用pthread_key_delete来取消键与线程私有数据之间的关联关系。

[cpp]  view plain  copy
  1. #include<pthread.h>  
  2. int pthread_key_delete(pthread_key_t *key);  
  3. //成功则返回0,否则返回错误编号。    
pthread_key_delete并不会激活与键相关的析构函数。

键一旦创建,就可以用过调用pthread_setspecific函数把键和线程私有数据关联起来,可以通过pthread_getspecific函数

获取线程私有数据的地址。

[cpp]  view plain  copy
  1. #include<pthread.h>  
  2. void *pthread_getspecific(pthread_key_t key); //返回线程私有数据值,没有值与键关联则返回NULL。  
  3. int pthread_setspecific(pthread_key_t key, const void *value); //成功则返回0,否则返回错误编号。  

实践:

[cpp]  view plain  copy
  1. #include<limits.h>  
  2. #include<string.h>  
  3. #include<pthread.h>  
  4. #include<stdlib.h>  
  5.   
  6. static pthread_key_t key;  
  7. static pthread_once_t init_done = PTHREAD_ONCE_INIT;  
  8. pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER;  
  9.   
  10. extern char **environ;  
  11.   
  12. static void thread_init(void){  
  13.     pthread_key_create(&key, free);  
  14. }  
  15.   
  16. char* getenv(const char *name){  
  17.     int i, len;  
  18.     char *envbuf;  
  19.   
  20.     pthread_once(*init_done, thread_init);  
  21.     pthread_mutex_lock(&env_mutex);  
  22.     envbuf = (char*)pthread_getspecific(key);  
  23.     if(envbuf == NULL){  
  24.         envbuf = malloc(ARG_MAX);  
  25.         if(envbuf == NULL){  
  26.             pthread_mutex_unlock(&env_mutex);  
  27.             return NULL;  
  28.         }  
  29.         pthread_setpecific(key,envbuf);  
  30.     }  
  31.     len = str;en(name);  
  32.     for(i=0; environ[i] != NULL; i++){  
  33.         if((!strncmp(name,environ[i],len) == 0) && (environ[i][len] == '=')){  
  34.             strcpy(envbuf, &environ[i][len+1]);  
  35.             phread_mutex_unlock(&env_mutex);  
  36.             return envbuf;  
  37.         }  
  38.     }  
  39.     pthread_mutex_unlock(&env_mutex);  
  40.     return NULL;  
  41. }  
这个函数根据环境变量的名字获取名字对应的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1 目标检测的定义 目标检测(Object Detection)的任务是找出图像中所有感兴趣的目标(物体),确定它们的类别和位置,是计算机视觉领域的核心问题之一。由于各类物体有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具有挑战性的问题。 目标检测任务可分为两个关键的子任务,目标定位和目标分类。首先检测图像中目标的位置(目标定位),然后给出每个目标的具体类别(目标分类)。输出结果是一个边界框(称为Bounding-box,一般形式为(x1,y1,x2,y2),表示框的左上角坐标和右下角坐标),一个置信度分数(Confidence Score),表示边界框中是否包含检测对象的概率和各个类别的概率(首先得到类别概率,经过Softmax可得到类别标签)。 1.1 Two stage方法 目前主流的基于深度学习的目标检测算法主要分为两类:Two stage和One stage。Two stage方法将目标检测过程分为两个阶段。第一个阶段是 Region Proposal 生成阶段,主要用于生成潜在的目标候选框(Bounding-box proposals)。这个阶段通常使用卷积神经网络(CNN)从输入图像中提取特征,然后通过一些技巧(如选择性搜索)来生成候选框。第二个阶段是分类和位置精修阶段,将第一个阶段生成的候选框输入到另一个 CNN 中进行分类,并根据分类结果对候选框的位置进行微调。Two stage 方法的优点是准确度较高,缺点是速度相对较慢。 常见Tow stage目标检测算法有:R-CNN系列、SPPNet等。 1.2 One stage方法 One stage方法直接利用模型提取特征值,并利用这些特征值进行目标的分类和定位,不需要生成Region Proposal。这种方法的优点是速度快,因为省略了Region Proposal生成的过程。One stage方法的缺点是准确度相对较低,因为它没有对潜在的目标进行预先筛选。 常见的One stage目标检测算法有:YOLO系列、SSD系列和RetinaNet等。 2 常见名词解释 2.1 NMS(Non-Maximum Suppression) 目标检测模型一般会给出目标的多个预测边界框,对成百上千的预测边界框都进行调整肯定是不可行的,需要对这些结果先进行一个大体的挑选。NMS称为非极大值抑制,作用是从众多预测边界框中挑选出最具代表性的结果,这样可以加快算法效率,其主要流程如下: 设定一个置信度分数阈值,将置信度分数小于阈值的直接过滤掉 将剩下框的置信度分数从大到小排序,选中值最大的框 遍历其余的框,如果和当前框的重叠面积(IOU)大于设定的阈值(一般为0.7),就将框删除(超过设定阈值,认为两个框的里面的物体属于同一个类别) 从未处理的框中继续选一个置信度分数最大的,重复上述过程,直至所有框处理完毕 2.2 IoU(Intersection over Union) 定义了两个边界框的重叠度,当预测边界框和真实边界框差异很小时,或重叠度很大时,表示模型产生的预测边界框很准确。边界框A、B的IOU计算公式为: 2.3 mAP(mean Average Precision) mAP即均值平均精度,是评估目标检测模型效果的最重要指标,这个值介于0到1之间,且越大越好。mAP是AP(Average Precision)的平均值,那么首先需要了解AP的概念。想要了解AP的概念,还要首先了解目标检测中Precision和Recall的概念。 首先我们设置置信度阈值(Confidence Threshold)和IoU阈值(一般设置为0.5,也会衡量0.75以及0.9的mAP值): 当一个预测边界框被认为是True Positive(TP)时,需要同时满足下面三个条件: Confidence Score > Confidence Threshold 预测类别匹配真实值(Ground truth)的类别 预测边界框的IoU大于设定的IoU阈值 不满足条件2或条件3,则认为是False Positive(FP)。当对应同一个真值有多个预测结果时,只有最高置信度分数的预测结果被认为是True Positive,其余被认为是False Positive。 Precision和Recall的概念如下图所示: Precision表示TP与预测边界框数量的比值 Recall表示TP与真实边界框数量的比值 改变不同的置信度阈值,可以获得多组Precision和Recall,Recall放X轴,Precision放Y轴,可以画出一个Precision-Recall曲线,简称P-R
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值