2

我正在 Linux 上创建一个 C 库,它有几个函数,它们一起对一些全局数据进行操作。为了使这些函数成为线程安全的,它们必须在代码中的适当位置使用互斥锁。

在 Linux 中,为了在应用程序中使用 pthread,需要链接到适当的库-lpthread。对于我的库一旦编译,我想让它的用户决定在他们的应用程序中使用 pthreads 以及如果他们不使用它。

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

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

在这一点上,我注意到FreeBSD 的 popen() 函数在第 66 行和第 67 行使用了一个不可移植的检查 - isthreaded来确定是否使用了线程,以及是否使用互斥锁。我怀疑这样的事情是否以任何方式标准化。但更重要的是,如果无法识别符号,则此类代码无法编译和链接,而在 Linux 中,如果未链接 pthread,则互斥符号甚至不会出现。

总结一下:在 Linux 上,如何创建一个库,它知道何时还使用线程,如果是,则在适当的情况下使用互斥锁,并且不需要链接到 pthread,除非应用程序开发人员特别想在某处使用线程?

4

2 回答 2

3

经过一些测试,Linux似乎已经自动完成了我想要的!如果您使用线程,则只需要链接 pthreads,而不是只需要 pthread mutex 支持。

在这个测试案例中:

#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 的情况下编译它时,我看到“互斥锁锁定!” 两次。这表明 pthread_mutex_lock() 本质上是一个非操作。但是在链接 pthreads 后,运行此应用程序将在第一次“互斥锁锁定!”后停止。被打印。

因此,我可以在我的库中适当地使用互斥锁,并且不需要使用 pthreads,并且在不需要的地方没有(显着?)开销。

于 2013-10-12T23:22:05.933 回答
0

通常的解决方案是:

  1. 在构建时使用#define开关来控制是否调用 pthreads 函数,并让您的构建过程创建两个版本的库:一个支持 pthread,另一个不支持,名称不同。依靠您图书馆的用户来链接正确的图书馆。

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

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

glibc 再次做了一些不同的事情——它使用带有惰性绑定符号的技巧来调用 pthreads 函数,前提是它们被链接到二进制文件中。但是这不是可移植的,因为它依赖于 pthread 的 glibc 实现的特定细节。参见 的定义__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
于 2013-10-12T23:25:27.207 回答