c++如何编写线程安全的DLL
DLL有个共同的特点就是都有一个初始化函数,一个资源释放函数,其他几个函数都是核心功能函数。而且这些DLL有时会被多个进程同时调用,这就牵扯到多进程的多线程调用DLL的问题。有点绕口,以下我根据我实践中遇到的问题,分四种情况分享一下我解决此类问题的经验:
1、动态库只有一个导出函数。
这种情况非常少,也是最容易处理的情况。这种情况下编写函数时,只需要考虑不要有冲突的全局数据就可以了。这里的全局数据包括了在堆中分配的数据块和静态全局变量等。如果存在这样的全局数据,那么进程中的不同线程访问这个函数就会造成冲突。
解决办法也很简单,就是尽量用堆栈(stack)来解决问题。由于堆栈的所有人是线程,所以它必然是线程安全的。当然也要注意避免堆栈溢出。
我们都知道,如果要在函数再次调用时保留前一次调用的状态,可以使用静态变量。但如果你要保持函数的线程安全,那么静态变量是不能用的,因为静态变 量是全局的,是属于进程的,也就是属于进程内线程共享的。所以如果确实需要在同一线程中保持函数的状态,相当于在不同次调用间传递参数,可以考虑使用静态 全局线程局部变量,即:
__declspec( thread ) int tls_i = 1;
该变量定义就使编译器保证了tls_i是对应于每个线程的,即每个线程都一个tls_i的副本(copy),这样必然就是线程安全的。
2、动态库导出了多个函数,而且多个函数间存在数据传递。
就像前面说的,一般DLL都导出多个函数,一个初始化,一个资源释放,其他为核心功能函数。这些函数间极有可能发生数据传递。如果一个初始化函数是 在线程A中调用的,而核心功能函数是在线程B中调用的,那么线程A初始化函数的资源就无法对应线程B中的核心功能,此外还有核心功能函数间的数据传递,这 样的DLL就不是线程安全的,必然导致错误。
解决办法是由用户(即使用DLL的人)保证这些导出函数是在一个线程中调用。但这样会很大程度上限制接口的设计和用户的使用自由度。所以最好的方法是函数只管自己的线程安全,不同函数传递数据用动态TLS,线程局部存储。
比如:
我在全局定义了一个变量,用于存储当前线程局部存储的index ID。
__declspec( thread ) int tls_i = 1;
当 调用分配资源的函数时,调用动态TLS函数TlsAlloc,分配一个ID,将其记录在全局的线程安全的tls_i变量,并通过TlsSetValue函 数将数据保存在线程安全的区域;当调用获取资源的函数时,通过TlsGetValue获取资源,处理完成后,调用Tlsfree对TLS index释放,以便新线程占有。
这样,只要DLL中每个函数保证其局部是线程安全的,函数间传递数据通过TLS(静态和动态),就可以实现整个DLL的线程安全。
3、限制访问DLL中某一函数的线程数目。
有时候,对于DLL中的某一个函数的访问线程数目是有限制的,超过了限制其他线程就得等一定的时间,一定的时间过后如果还不能得到执行机会,那就返回超时。这样的设计对用户来说是友好的,而且很实用,有的商业程序确实是按照允许用户访问的通道数目来计价的。
对DLL中的函数做这样的一个封装,一般是简单的待用Semaphore信号量,来解决。DLL初始化时调用CreateSemaphore函数对信号量进行初始化,其原型如下:
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
// pointer to security attributes
LONG lInitialCount, // initial count
LONG lMaximumCount, // maximum count
LPCTSTR lpName // pointer to semaphore-object name
);
对 于信号量,它每WaitForSingleObject一次(当然是要进入),其状态值(一个整数)就减1,使用完ReleaseSemaphore其状 态值就加1,当其状态值为0时信号量就由有信号变为无信号。利用信号量的这一特性,我们在初始化时将信号量的初始值(第2个参数)设置为限制的线程访问数 目。在要限制访问线程数目的函数内部,通过调用WaitForSingleOject获取控制权,并指定一个等待时间(这个由配置文件指定),根据情况超 时返回,使用完ReleaseSemaphore释放对占用,让其他线程可以调用这个函数。
4、多进程情况下的多线程安全DLL。
前面3讲了有时候需要对某一函数的访问线程进行限制,而我们知道,DLL是可以被多个进行加载并调用的。那就是说如果我们只对一个进程进行了限制,那么在多进程调用的情况下,这样的限制被轻易攻破。
我们都知道,Semaphore信号量属于内核对象,也就是说其可以被多进程共享访问,也就说,如果我们给一个Semaphore指定了一个名字,在另一个进程中,我们只要调用OpenSemaphore函数用同一名字打开信号量就可以访问了。这样问题就解决了?
现实情况是,多进程情况下,一般不是简单的多进程共享一个Semaphore就可以了。多进程间需要互通很多信息。一般的解决办法是,采用共享数据段。
#pragma data_seg("share")
int share_data=0;//需要初始化,否则全局变量被放到BSS段中,从而导致共享失败
#pragma data_seg()
#pragma comment(linker,"/SECTION:share, RWS") //如果不加这句,只是单纯的说明他们是放到数据段中,还没有共享
通过pragam编译器指令生成了一个名叫share的共享数据段,这样对于变量share_data就可以多进程共享的了。如果要多进程间交换数据,只要在data_seg中添加数据定义即可。
转载于:https://www.cnblogs.com/duyy/p/3741829.html
c++如何编写线程安全的DLL相关推荐
- 写缓存java,编写线程安全的Java缓存读写机制 (原创)
一种习以为常的缓存写法: IF value in cached THEN return value from cache ELSE compute value save value in cache ...
- C/C++:Windows编程—创建进程、终止进程、枚举进程、枚举线程、枚举DLL
创建进程的2种方式 1. 创建进程最简单的方法 UINT WINAPI WinExec(_In_ LPCSTR lpCmdLine, // 指向可执行文件_In_ UINT uCmdShow // 程 ...
- Windows核心编程_远线程方式实现Dll注入
之前有介绍过HOOK的方式注入,这次介绍以其它方式注入,而无须HOOK,要知道在Windows这个浩荡的海洋里,API就是宝藏,找到足够多的宝藏那么你就是海贼王~! 实现思路如下: 首先打开一个进程的 ...
- (二) 使用Detours调试远程线程注入的dll
远程线程注入是指一个进程在另一个进程中创建线程的技术.该技术可以用于:API Hook,破解软件所谓的"内存补丁"等. 将DLL注入到其它进程并不是难事,问题是这个被注入的DLL不 ...
- 编写线程安全的Java缓存读写机制 (原创)
一种习以为常的缓存写法: IF value in cached THENreturn value from cache ELSEcompute valuesave value in cacheretu ...
- php 编写线程教程,php 实现多线程
通过php的Socket方式实现php程序的多线程.php本身是不支持多线程的,那么如何在php中实现多线程呢?可以想一下,WEB服务器本身都是支持多线程的.每一个访问者,当访问WEB页面的时候,都将 ...
- Java植物名录程序_程序员用Java语言编写多线程应用程序,程序员能控制的关键性工作有两个方面:一是编写线程的_________方法;二是建立线程实例。...
沟通的目的是打造"3G团队",其中的"3G"具体指: "啊,时间过得真快啊!"中的"啊" 活塞与气缸盖.气缸壁共同组成燃 ...
- java 编写线程公共类_Java实现线程间通信方式
线程间通信的模型: 共享内存 消息传递 我们来做道题理解一下 题目: 有两个线程A.B,A线程向一个集合里面依次添加元素"abc"字符串,一共添加十次,当添加到第五次的时候,希望B ...
- 通过编写自定义的gina.dll实现U盘开机锁
simples.c 编译为可执行程序simples.exe 下面的程序编译为ginafuncs.dll并用它替换c:\windows\system32下的msgina.dll 开机时ginafuncs ...
最新文章
- linux驱动:音频驱动(六)ASoc之codec设备
- QFIL工具如何导出手机分区数据
- zTree的调用设使用(跨两个系统,两类技术实现的项目案例SpringMVC+Spring+MyBatis和Struts2+Spring+ibatis框架组合)
- JS ES6中export和import详解
- 老歌新唱--使用VB6开发的ActiveX实现.NET程序的混淆加密
- 关于 C语言的 按位取反 ~
- nsa构架_我如何使用NSA的Ghidra解决了一个简单的CrackMe挑战
- 判断sem信号量为零_kernel.sem信号量调优
- 纪念概率学界最后一位集大成者——钟开莱
- 【转载】project2019安装教程
- matlab中的对数函数,[matlab对数函数]对数函数运算法则是什么呢?
- Java文件拒绝访问问题
- win10安装navisworks失败,怎么强力卸载删除注册表并重新安装
- bzoj1127 [POI2008]KUP
- 《软件随想录-Joel on Software》书摘
- 多行文字显示不完用省略号表示
- 免费的B站短链生成器,将链接转成b23.tv
- 唯美计算机语言,唯美精辟的语句
- 南林计算机科学,南京林业大学信息科学技术学院
- 大佬是如何从头写一篇顶级论文的?
热门文章
- 我的docker随笔9:docker在centos上的安装
- 数组乱码_python 爬虫随笔-土办法治乱码
- 【Flink】 Flink 应用资源分配问题排查思路
- 95-910-165-源码-FlinkSQL-Flink SQL 中的时间属性
- 【Elasticsearch】Elasticsearch 存储桶聚合
- 【Flink】Flink1.12.0 FlinkSQL消费Kafka 使用 temporal join 关联维表Hive 最新分区数据 join 不上
- 【ElasticSearch】Es 源码之 AutoFollowCoordinator 源码解读
- 【ElasticSearch】Es 源码之 AliasValidator 源码解读
- 【kafka】JMX 监控kafka kafka rmi NoSuchObjectException no such object in table
- 【Kafka】 kafka 启动 Connection to node 1 could not be established. Broker may not be available