摘要

在上一篇博客《STL空间配置器那点事》简单介绍了空间配置器的基本实现

两级空间配置器处理,一级相关细节问题,同时简单描述了STL各组件之间的关系以及设计到的设计模式等。

在最后,又关于STL空间配置的效率以及空间释放时机做了简单的探讨。

线程安全问题概述

为什么会有线程安全问题?

  认真学过操作系统的同学应该都知道一个问题。

  first--进程是系统资源分配和调度的基本单位,是操作系统结构的基础,是一个程序的运行实体,同时也是一个程序执行中线程的容器

  seconed--进程中作为资源分配基本单位,管理着所有线程共享资源:代码段,数据段,堆,部分共享区(IPC中的共享内存等)。。栈则是线程私有的。

所以,由此就有:如果我们的数据存放位置处在数据段,堆这两个地方,那么就会有线程安全问题:

 #include <iostream>
using namespace std;
static int * arr = new int(4); //arr作为全局变量存在于数据段,new申请所得空间存在于堆上。 void testThreadSafe(int arg)
{
*arr = arg;
} int main()
{
int arg;
cin >> arg;
testThreadSafe(arg);
cout << (*arr)<<endl;
return ;
}

  做个简单分析,假设进程同时运行到了第七行,因为程序执行的最小粒度是更为细致的cpu指令而不是一个代码语句。

所以可能A线程和B线程同时执行修改*arr = arg;,但是两个线程中cin>>arg输入的值不一样,那么就有问题。

两个线程各自执行到15行时,显示的结果是一样的(因为线程共享该区域),但他们本来却不该相同。

这就是线程安全问题。

STL中线程安全问题的存在  

STL中,一级空间配置器简单封装malloc,free同时引入sethandler机制。而malloc,free作为最基本的系统调用是线程安全的,
所以问题就在二级空间配置器的实现部分了。

  各位还记得二级配置器内部结构定义吧。

template <bool threads, int inst>
class __DefaultAllocTemplate
{
//...
protected: //桶结构,保存链表
static _Obj* _freeList[_NFREELISTS];
//.....
};

这里的核心结构,保存自由链表的指针数组就是各静态数据,存在于数据段,于是就有了线程安全问题。

线程安全问题的解决方案之一:

linux环境,互斥锁

win环境,临界区(临界资源访问问题)

  对于STL的二级空间配置器中,线程安全问题的唯一存在也就是对于已组织的自由链表的访问了(也就是Allocate和Deallocate了):
两个线程同时向空间配置器申请内存块(ps,A未完成取出该节点并将表指针指向下一个节点时,B线程来了。于是两个线程同时得到一块内存);

//////A执行玩1,尚未执行2,B就来申请空间。最终两个线程都修改数组中指针指向y,且共同拥有x

两个线程同时向空间配置器释放内存块;

  

////a释放执行1而没有来得及执行2,于是乎,在1。5的情况系,b释放,进入。于是,最终结果,a块,b块都指向了x,但是数组中指针只是指向了后来修改他的值,于是就有了内存泄漏。

解决方案,互斥锁使用

核心代码给出:

文件Alloc.h中部分代码

#pragma once

#include "Config.h"
#include "Trace.h" #include "Threads.h" #ifdef __STL_THREADS
#define __NODE_ALLOCATOR_THREADS true //用于二级空间配置器翻非类型模板参数 #define __NODE_ALLOCATOR_LOCK \
{ if (threads) _S_node_allocator_lock._M_acquire_lock(); }
#define __NODE_ALLOCATOR_UNLOCK \
{ if (threads) _S_node_allocator_lock._M_release_lock(); }
#else
// Thread-unsafe
# define __NODE_ALLOCATOR_LOCK
# define __NODE_ALLOCATOR_UNLOCK
# define __NODE_ALLOCATOR_THREADS false
#endif # ifdef __STL_THREADS
static _STL_mutex_lock _S_node_allocator_lock;
# endif template <bool threads, int inst>
class __DefaultAllocTemplate
{ class _Lock;
friend class _Lock;
class _Lock {
public:
_Lock()
{
__TRACE("锁保护\n");
__NODE_ALLOCATOR_LOCK;
}
~_Lock()
{
__TRACE("锁撤销\n");
__NODE_ALLOCATOR_UNLOCK;
}
};
static void* Allocate(size_t n)
{
void * ret = ;
__TRACE("二级空间配置器申请n = %u\n",n);
if(n>_MAX_BYTES)
ret = MallocAlloc::Allocate(n); _Obj* volatile * __my_free_list = _freeList + _FreeListIndex(n); //利用RAII(资源获取即初始化原则)进行封装,保证 即使内部抛出异常,依旧执行解锁操作
#ifdef __STL_THREADS
_Lock __lock_instance;
#endif
_Obj* __result = *__my_free_list; if (__result == )
ret = _Refill(RoundUp(n));
else
{
*__my_free_list = __result -> _freeListLink;
ret = __result;
}
return ret;
} static void Deallocate(void* p, size_t n)
{
if(!p)
{
return;
}
__TRACE("二级空间配置器删除p = %p,n = %d\n",p,n);
if (n > (size_t) _MAX_BYTES)
MallocAlloc::Deallocate(p, n);
else
{
_Obj* volatile* __my_free_list = _freeList + _FreeListIndex(n);
_Obj* q = (_Obj*)p; #ifdef __STL_THREADS
//进行资源归还自由链表时的锁操作。
_Lock __lock_instance;
#endif
q -> _freeListLink = *__my_free_list;
*__my_free_list = q;
}
}

文件Threads.h

#pragma once 

#if defined(__STL_PTHREADS)
#include <pthread.h>
#endif #include "Config.h" __STLBEGIN struct _STL_mutex_lock
{ #if defined(__STL_PTHREADS)
pthread_mutex_t _M_lock;
void _M_initialize() { pthread_mutex_init(&_M_lock, NULL); }
void _M_acquire_lock() { pthread_mutex_lock(&_M_lock); }
void _M_release_lock() { pthread_mutex_unlock(&_M_lock); }
#else /* No threads */
void _M_initialize() {}
void _M_acquire_lock() {}
void _M_release_lock() {}
#endif
}; __STLEND

简单测试结果

其中TRACE打印的“锁保护”,“锁撤销” 部分就是二级空间配置器资源分配时锁机制的保护实现了。

  其利用了C++的RAII(资源获取即初始化方案)  

  同时利用C++对象特性,退出作用域即执行析构函数,将解锁封装,巧妙的避免了死锁问题的产生

死锁:简单理解就是,因为某个线程锁定资源进行访问时,因为异常等原因退出执行,但是没来的及解锁,致使其他线程都无法访问共享资源的现象就是死锁。更细致的解释请找google叔。

最后,说明的是,实际的STL源码中因为需要考虑平台,系统兼容性等问题,对于锁的使用通过宏编译技术,有比较长的一段代码,我这里只是取出了当下linux平台可用代码放在了自己的Threads.h

更详细代码请关注个人另一博客:http://www.cnblogs.com/lang5230/p/5556611.html

或者github获取更新中的代码:https://github.com/langya0/llhProjectFile/tree/master/STL

stl空间配置器线程安全问题补充的更多相关文章

  1. 【转】STL空间配置器

    STL空间配置器(allocator)在所有容器内部默默工作,负责空间的配置和回收.STL标准为空间配置器定义了标准接口(可见<STL源码剖析>P43).而具体实现细节则由各编译器实现版本 ...

  2. STL空间配置器

    1.什么是空间配置器? 空间配置器负责空间配置与管理.配置器是一个实现了动态空间配置.空间管理.空间释放的class template.以内存池方式实现小块内存管理分配.关于内存池概念可以点击:内存池 ...

  3. 咬碎STL空间配置器

    STL空间配置器 一.开场白: 给我的感觉就是,了解是空间配置器的功能,是那么的明了:在看原理,我还是很开心:接下来是360度大转变: 那么长的变量或者函数命名.那么多的宏.不爽,不过,遇上我这种二货 ...

  4. STL空间配置器那点事

    STL简介 STL(Standard Template Library,标准模板库),从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其 ...

  5. STL空间配置器解析和实现

    STL空间配置器的强大和借鉴作用不言而喻,查阅资料,发现了Dawn_sf已经对其有了极其深入和详细的描述,所以决定偷下懒借用其内容,只提供自己实现STL空间配置器的源码,具体解析内容参考:(一)STL ...

  6. 【陪你系列】5 千字长文+ 30 张图解 | 陪你手撕 STL 空间配置器源码

    大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub https://github.com/rongweihe/MoreT ...

  7. stl空间配置器alloc

    new运算包含两阶段操作: 1) 调用::operator new分配内存     2) 调用构造函数构造对象内容 delete运算包含两阶段操作: 1)调用析构函数将对象析构    2)调用::op ...

  8. STL空间配置器源码分析(四)bitmap_allocator

    一.摘要 bitmap_allocator是STL空间分配器的其中一种,它采用内存池策略,最多存储64条空闲链表(freelist,实际是一块空间连续的内存区,后面也称为超级块),每条空闲链表存储的内 ...

  9. STL——空间配置器(构造和析构基本工具)

    以STL的运用角度而言,空间配置器是最不需要介绍的东西,它总是隐藏在一切组件(更具体地说是指容器,container)的背后,默默工作,默默付出.但若以STL的实现角度而言,第一个需要介绍的就是空间配 ...

随机推荐

  1. 使用Flask-Migrate进行管理数据库升级

    我们在升级系统的时候,经常碰到需要更新服务器端数据结构等操作,之前的方式是通过手工编写alter sql脚本处理,经常会发现遗漏,导致程序发布到服务器上后无法正常使用. 现在我们可以使用Flask-M ...

  2. apache 500错误

    一直以为开了error_log,没想没有加,于是折腾了好久. 开启error_log后,发现是xdebug的max_nesting_level值太小了. 还一个原因是.htaccess文件中的 < ...

  3. 使用MyXls,出现访问被拒绝情况

    MyXls 作为一款比较好用的 操作Excel 的开源项目. 最近日常偶然发现报权限的问题,导出xls失败,处理方式以下: 如报上图所示错误,则需要在web.config中 <system.we ...

  4. 红黑树(三)之 Linux内核中红黑树的经典实现

    概要 前面分别介绍了红黑树的理论知识 以及 通过C语言实现了红黑树.本章继续会红黑树进行介绍,下面将Linux 内核中的红黑树单独移植出来进行测试验证.若读者对红黑树的理论知识不熟悉,建立先学习红黑树 ...

  5. 剑指架构师系列-Linux下的调优

    1.I/O调优 CentOS下的iostat命令输出如下: $iostat -d -k 1 2 # 查看TPS和吞吐量 参数 -d 表示,显示设备(磁盘)使用状态:-k某些使用block为单位的列强制 ...

  6. Understanding CMS GC Logs--转载

    原文地址:https://blogs.oracle.com/poonam/entry/understanding_cms_gc_logs Understanding CMS GC Logs By Po ...

  7. 使用 Web Tracing Framework 分析富 JS 应用的性能

    来自谷歌的 Web Tracing Framework 包含一组工具和脚本,用于 JavaScript 相关代码的性能分析.它是重 JavaScript 应用程序的理想选择,而 JavaScript ...

  8. LeetCode-304. Range Sum Query 2D - Immutable

    Description: Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by ...

  9. Android 学习笔记之Volley(八)实现网络图片的数据加载

    PS:最后一篇关于Volley框架的博客... 学习内容: 1.使用ImageRequest.java实现网络图片加载 2.使用ImageLoader.java实现网络图片加载 3.使用NetWork ...

  10. SQL Server技术问题之自定义函数优缺点

    优点: 可以在SQL语句中调用,直接使用返回值,从而可以形成复杂的SQL应用. 缺点: 能在函数中使用的语句有严格限制: 不支持create.ALTER.drop等DDL(Data Definitio ...