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;
#pragma data_seg() 
#pragma comment(linker,"/SECTION:share, RWS")
通过pragam编译器指令生成了一个名叫share的共享数据段,这样对于变量share_data就可以多进程共享的了。如果要多进程间交换数据,只要在data_seg中添加数据定义即可。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java多线程可以调用DLL(Dynamic-Link Library)来进行一些特定操作。 首先,在Java中调用DLL需要使用到Java Native Interface(JNI)技术。JNI是Java提供的一种机制,用于在Java程序中调用本地的C/C++代码。通过JNI,我们可以在Java程序中加载和调用DLL中的函数。 在多线程中调用DLL的过程如下: 1. 首先,在Java中编写一个JNI的接口类,用于定义与DLL中函数的映射关系。这个接口类需要使用Java的native关键字来声明与DLL中函数对应的本地方法。 2. 在DLL中实现与接口类中声明的本地方法对应的C/C++函数。这些函数可以通过DLL导出符号的方式供Java程序调用。 3. 在Java程序中加载DLL并调用其中的函数。 使用多线程调用DLL时,需要注意以下几点: 1. 线程安全性:在多线程环境下调用DLL时,需要确保DLL中的函数是线程安全的,即能够正确处理多个线程间的共享数据。 2. 同步控制:如果DLL中的函数需要访问共享资源,需要在Java程序中使用同步控制机制,如synchronized关键字或Lock对象来确保线程之间的互斥访问。 3. 线程间通信:如果多个线程需要相互通信,可以通过共享内存或消息传递的方式实现。在DLL中可以使用线程同步的机制来实现线程间的互斥、等待和通知操作。 总之,Java多线程可以通过JNI技术调用DLL来完成一些底层的特定操作。在进行多线程调用DLL时,需要保证线程安全性,确保合适的同步控制和线程间通信方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值