pthread:销毁全局静态互斥锁

3

这段代码摘自Richard Stevens所著的《UNIX环境高级编程》第三版。这是一个制作可重入版本getenv()的示例。仅用于学习目的。

/* Copyright (c) W.R.Stevens */
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>

extern char **environ;

pthread_mutex_t env_mutex;

static pthread_once_t init_done = PTHREAD_ONCE_INIT;

static void
thread_init(void)
{
    pthread_mutexattr_t attr;

    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&env_mutex, &attr);
    pthread_mutexattr_destroy(&attr);
}

int
getenv_r(const char *name, char *buf, int buflen)
{
    int i, len, olen;

    pthread_once(&init_done, thread_init);
    len = strlen(name);
    pthread_mutex_lock(&env_mutex);
    for (i = 0; environ[i] != NULL; i++) {
        if ((strncmp(name, environ[i], len) == 0) &&
          (environ[i][len] == '=')) {
            olen = strlen(&environ[i][len+1]);
            if (olen >= buflen) {
                pthread_mutex_unlock(&env_mutex);
                return(ENOSPC);
            }
            strcpy(buf, &environ[i][len+1]);
            pthread_mutex_unlock(&env_mutex);
            return(0);
        }
    }
    pthread_mutex_unlock(&env_mutex);
    return(ENOENT);
}

这段代码易于理解,但我有一个问题。我们从未调用 pthread_mutex_destroy() 函数,这意味着在退出时可能会出现内存泄漏(我想它可能因平台而异)。
首先想到的是可以使用 PTHREAD_MUTEX_INITIALIZER。需要调用 pthread_mutex_init() 吗?如果不需要,则无需调用 pthread_mutex_destroy()。但是,互斥锁将是非递归的。
可以编写一个简单的 C++ 类,在析构函数中销毁互斥量。但这对只有 C 编译器的人来说并不合适(因为只有一个函数就使用 C++ 编译器似乎是荒谬的)。
另一个想到的是编译器特定的扩展,例如 GCC 中的 __attribute__((destructor))(并且希望 clang 也支持该特性)。但是这是不可移植的。
有可能避免内存泄漏吗?如果可以,如何在 C 中实现?
更新: 正如 David Butenhof 所写的 "Programming with POSIX threads" 中所述,我们永远不需要销毁 PTHREAD_MUTEX_INITIALIZER。那么其他属性的互斥锁呢?
2个回答

5

尽管一些简单工具将进程终止时仍然存在的资源视为内存泄漏,但实际上它们并不是内存泄漏。内存泄漏指程序在其生命周期中不可逆转且无限增长的资源需求,这种增长相对于实际工作集合来说是不成比例的。

根据 POSIX(这也是 POSIX 线程的来源),所有进程本地资源都会在程序终止时停止存在。没有必要显式地销毁 / 释放它们,在某些情况下,例如您的情况,您 不能安全地销毁/释放 它们,也 不应该试图这样做


2
+1 优秀的回答。这与“您必须在终止进程之前始终显式销毁所有资源”这种盲目模仿的做法不同,即使这很困难/危险并需要大量额外的代码和测试。 - Martin James

1
没有内存泄漏,因为pthread_mutex_t变量存在于用户内存中,在进程退出时,所有用户分配的内存都会被回收。内存泄漏发生在某些东西分配堆内存,比如strdup,然后你没有清理它。
如果像这样分配:
pthread_mutex_t *foo;
....
foo =malloc(sizeof(pthread_mutex_t);

然后,如果没有释放创建了内存泄漏的自由 -- 如果foo被分配给更多的新内存。然而,在进程终止时,包括堆在内的进程请求的所有内存都将被操作系统回收。Stevens在进程章节中解释了这一点。该代码不会泄漏。


我在考虑pthread_mutex_init()期间的分配问题。如果存在init()destroy(),我们需要关心调用它们吗? - ghostmansd
2
如果您的互斥锁的持续时间与整个程序一样长,那么您实际上不需要同时调用它们。但是想象一下,如果您正在创建一个包含互斥锁的链接列表,并且仅在一个函数中使用该列表,然后释放它。那么您需要确保调用pthread_mutex_destroy(),否则如果您在程序过程中多次调用该函数,这些泄漏将会累积。 - Crowman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接