pthreads:如何断言代码是在单线程上下文中运行的

时间:2012-12-20 19:55:41

标签: c multithreading pthreads posix fork

我正在编写一个在初始化期间需要fork()的C库。因此,我想断言()应用程序代码(在我的控制之外)从单个线程上下文调用我的库初始化代码(以避免众所周知的“threads and fork don't mix”问题)。一旦我的库被初始化,它就是线程安全的(并且期望应用程序级代码可以创建线程)。我只关心支持pthreads。

使用pthreads计算当前进程空间中的线程数似乎是不可能的。实际上,即使googletest只在Mac OS和QNX上实现GetThreadCount()

鉴于我无法统计线程,我是否可以在某种程度上断言单线程上下文?

澄清:如果可能的话,我想避免使用“/ proc”(非可移植),一个额外的库依赖(如libproc)和LD_PRELOAD样式的pthread_create包装。

澄清#2:在我的情况下,使用多个进程是必要的,因为我的库中的工作人员相对较重(使用webkit)并且可能会崩溃。但是,我希望原始流程能够在工人崩溃中幸存下来。

3 个答案:

答案 0 :(得分:2)

您可以将库初始化函数标记为在应用程序main()之前运行。例如,使用GCC,

static void my_lib_init(void) __attribute__((constructor));

static void my_lib_init(void)
{
    /* ... */
}

另一种选择是使用posix_spawn()来分叉和执行工作进程作为单独的从属二进制文件。

编辑添加:

在我看来,如果你想确定进程是否已经创建(实际的,基于内核的)线程,你将不得不依赖于特定于操作系统的代码。

在Linux的情况下,确定很简单,并且在其他操作系统上也可以安全运行。如果它无法确定当前进程使用的线程数,则该函数将返回-1:

#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>

int count_threads_linux(void)
{
    DIR           *dir;
    struct dirent *ent;
    int            count = 0;

    dir = opendir("/proc/self/task/");
    if (!dir)
        return -1;

    while (1) {

        errno = 0;
        ent = readdir(dir);
        if (!ent)
            break;

        if (ent->d_name[0] != '.')
            count++;
    }

    if (errno) {
        const int saved_errno = errno;
        closedir(dir);
        errno = saved_errno;
        return -1;
    }

    if (closedir(dir))
        return -1;

    return count;
}

在某些情况下(例如没有/proc/的chroot),即使在Linux中该检查也会失败,因此-1返回值应始终被视为 unknown 而不是错误(尽管errno将指出失败的实际原因。)

查看FreeBSD手册页,我想知道相应的信息是否可用。

最后:

我不是尝试检测有问题的案例,而是强烈建议您使用fork()exec()(或posix_spawn())从属进程,仅使用异步信号安全函数(请参阅{{ 3)})在子进程中(exec()之前),从而避免fork() - 线程并发症。在forking()之前,您仍然可以创建任何共享内存段,套接字对等。我能看到的唯一缺点就是你必须为奴隶工人使用单独的二进制文件。鉴于你对它们的描述,这对我来说听起来不是一个缺点。

答案 1 :(得分:1)

如果向进程'控制tty发送SIGINFO信号,则该进程应描述线程的状态。从描述中可以推断出是否已经创建了任何线程。

您可能需要开发一个通过popen调用的小工具来将输出读回到您的库中。

添加了示例代码Fri Dec 21 14:45

运行一个创建五个线程的简单程序。线程基本上睡觉了。在程序退出之前,发送SIGINFO信号以获取线程的状态。

openbsd> cat a.c
#include <unistd.h>
#include <pthread.h> 

#define THREADS 5

void foo(void);

int
main()
{    
    pthread_t thr[THREADS];
    int j;

    for (j = 0; j < THREADS; j++) {
        pthread_create(&thr[j], NULL, (void *)foo, NULL);
    }   

    sleep(200);                             

    return(0);
}             

void
foo()
{   
    sleep(100);
}             
openbsd> gcc a.c -pthread
openbsd> a.out &
[1] 1234
openbsd> kill -SIGINFO 1234
 0x8bb0e000 sleep_wait  15 -c---W---f 0000          
 0x8bb0e800 sleep_wait  15 -c---W---f 0000          
 0x8bb0e400 sleep_wait  15 -c---W---f 0000          
 0x7cd3d800 sleep_wait  15 -c---W---f 0000          
 0x7cd3d400 sleep_wait  15 -c---W---f 0000          
 0x7cd3d000 sleep_wait  15 -c---W---f 0000 main  

答案 2 :(得分:-1)

你可以使用use pthread_once()来确保没有其他线程做同样的事情:这样你就不必关心多个线程正在调用你的初始化函数,只有一个线程会真正执行。

让您的公开初始化功能通过pthread_once()进行私有初始化。

static pthread_once_t my_initialisation_once = PTHREAD_ONCE_INIT;

static void my_initialisation(void)
{
    /* do something */
}

int lib_initialisation(void)
{
    pthread_once(&my_initialisation_conce, my_initialisation);

    return 0;
}

可以找到另一个例子here

<强>链接