2013-10-12 17 views
2

我在Linux上创建了一个C库,它有几个功能,它们共同操作一些全局数据。为了使这些函数成为线程安全的,他们必须在代码中的适当位置使用互斥锁。如何创建仅在pthread链接时才使用互斥锁的库?

在Linux中,为了在应用程序中使用pthreads,需要链接到相应的库中,即-lpthread。在我的库被编译的情况下,如果它的用户决定在他们的应用程序中使用pthread,并且他们不这样做,我想让它工作。

在开发人员在其应用程序中不使用线程的情况下,它们将不会链接到pthread。因此,我希望我编译的库不要求它,此外,在单线程应用程序中使用互斥锁使用不必要的开销(更不用说是愚蠢的)。

是否有某种方式来编写代码(如果需要,使用GCC扩展)只有某些符号链接到某个代码块才会运行?我知道我可以使用dlopen()和朋友,但这本身需要一些我试图避免的。我想我所寻找的东西必须存在,因为几个标准函数在同一条船上,并且需要互斥体是线程安全的(而且它们是),但是即使不与pthread链接,也可以工作。

在这一点上,我注意到,FreeBSD's popen() function上线66 & 67采用非便携式检查 - isthreaded,以确定是否有使用或不使用线程,以及是否使用互斥。我怀疑任何这样的东西都是以任何方式标准化的。但更重要的是,如果符号不被识别,这种代码就无法编译和链接,在Linux中,如果pthread未链接,互斥符号甚至不会出现。总结:在Linux上,如何创建一个库,它知道何时使用线程,如果是,在适当的情况下使用互斥锁,并且不需要链接到pthread,除非应用程序开发人员特别想使用线程在某处?

+0

“在开发人员在应用程序中不使用线程的情况下,它们不会与pthreads链接。” - 但你的图书馆*将*。或者如果它是静态库,它将包含大量必须由调用程序解决的外部pthread引用,即使它们不使用pthread。如果没有一对pthread/no-pthread共享对象模块,我不认为你会得到你想要的东西,正确的一个dyna-在运行时加载*由你根据调用者的初始化配置加载,所有在非线程配置中的锁定操作。 – WhozCraig

+0

你可以随时踢,顺便说一句,并让应用程序提供一组锁*为你*。例如,RSA BSAFE CryptoC-ME通过要求应用程序提供一个回调函数,该函数忠实地使用它自己的“锁定表”,其大小为由库标题指定的预定义的“#define”大小,然而许多“锁“是必要的。每个锁操作都通过索引传递给回调函数,其中一个var声明操作请求是(LOCK,UNLOCK)。然后,应用程序可以知道它是单线程的,只需提供一个回调,该回调总是返回true,而不会执行其他任何操作。 – WhozCraig

回答

3

经过一番测试,似乎Linux已经做了我想要的自动!如果你使用线程,你只需要链接到pthreads,而不是如果你只是想要pthread互斥体支持。

在这个测试用例:“互斥锁”

#include <stdio.h> 
#include <errno.h> 
#include <pthread.h> 

int main() 
{ 
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 

    if (!(errno = pthread_mutex_lock(&mutex))) { puts("Mutex locked!"); } 
    else { perror("Could not lock mutex"); } 

    if (!(errno = pthread_mutex_lock(&mutex))) { puts("Mutex locked!"); } 
    else { perror("Could not lock mutex"); } 

    return 0; 
} 

在编制本没有挂并行线程,我看两次。这表明pthread_mutex_lock()本质上是非操作。但是,如果链接了pthread,运行此应用程序将在第一次“Mutex锁定!”后停止。被打印。

因此,我可以在适当的情况下在我的库中使用互斥锁,并且不需要使用pthreads,并且不需要它(signifigant?)开销。

+0

我在各种方便的BSD上测试了它。 FreeBSD和NetBSD与Linux有相同的语义,这很好,OpenBSD需要-lpthread。另一方面,与-lpthread链接的FreeBSD和OpenBSD都没有挂在第二个锁上,但是避免了返回的死锁。所有BSD似乎也在中定义__histhreaded,以便可以检查是否有任何线程正在使用。 – Joe

+0

关于__isthreaded,结果证明NetBSD缺少它,就像Debian kFreeBSD一样。 DragonFlyBSD原来与FreeBSD相匹配。对于OpenBSD,为了实现我想要的,需要使用:int pthread_mutex_lock(pthread_mutex_t * mutex)__attribute __((weak)); – Joe

0

通常的解决方案是:

  1. 使用#define开关在编译的时候来控制是否调用并行线程功能与否,和你的构建过程中创建库的两个版本:一个并行线程感知一个不是,名字不同。依靠你的图书馆的用户链接到正确的。

  2. 不要直接调用pthreads函数,而是调用用户提供的lockunlock回调(如果需要,也可以调用线程本地存储)。库用户负责分配和调用适当的锁定机制,这也允许他们使用非线程线程库。

  3. 什么都不做,只是记录用户代码应该确保你的库函数不能同时从多个线程进入。

的glibc做不同的东西再次 - 它使用与延迟绑定符号的技巧来调用并行线程功能,只有当它们被链接到二进制文件。这不是可移植的,因为它依赖于pthread的glibc实现的具体细节。请参阅definition of __libc_maybe_call()

#ifdef __PIC__ 
# define __libc_maybe_call(FUNC, ARGS, ELSE) \ 
    (__extension__ ({ __typeof (FUNC) *_fn = (FUNC); \ 
        _fn != NULL ? (*_fn) ARGS : ELSE; })) 
#else 
# define __libc_maybe_call(FUNC, ARGS, ELSE) \ 
    (FUNC != NULL ? FUNC ARGS : ELSE) 
#endif