英文:
When is pthread_mutex_destroy necessary?
问题
I understand. Here's the translation of the code-related part:
我明白了。这是代码相关部分的翻译:
我想询问 `pthread_mutex_t` 的正确使用方式。
我明白您可以使用 `pthread_mutex_init` 函数进行初始化,也可以将其设置为 `PTHREAD_MUTEX_INITIALIZER`,并且 `pthread_mutex_destroy` 会 "销毁" 互斥锁。
我想知道是否可以不调用 `pthread_mutex_destroy`,以及在什么情况下是必要的?我在这里找到了规范:[这里](https://pubs.opengroup.org/onlinepubs/009604599/functions/pthread_mutex_destroy.html)。
这是我的特定用例:
```c
struct entries;
struct hash_table {
struct hash_table_entry entries[HASH_TABLE_CAPACITY];
pthread_mutex_t fine_grain[HASH_TABLE_CAPACITY];
};
struct hash_table *hash_table_create() {
struct hash_table *hash_table = calloc(1, sizeof(struct hash_table));
for (size_t i = 0; i < HASH_TABLE_CAPACITY; ++i) {
...
hash_table->fine_grain[i] = PTHREAD_MUTEX_INITIALIZER;
}
return hash-table
}
我之所以提问,是因为项目负责的助教曾经说过:
使用锁后,请确保销毁它,我们会检查内存泄漏
这不是指分配的哈希表,而是指个别的锁,所以我想知道为什么可能需要使用 pthread_mutex_destroy
。
<details>
<summary>英文:</summary>
I want to ask the proper usage of `pthread_mutex_t`.
I understand that you can either initialize by using the `pthread_mutex_init` function or setting it equal to `PTHREAD_MUTEX_INITIALIZER`and that `pthread_mutex_destroy` "destroys" the mutex.
I am wondering about if it is okay not to call `pthread_mutex_destroy` and in what circumstances it is necessary? I found the specification here: [here](https://pubs.opengroup.org/onlinepubs/009604599/functions/pthread_mutex_destroy.html).
Here is my specific use case:
struct entries;
struct hash_table {
struct hash_table_entry entries[HASH_TABLE_CAPACITY];
pthread_mutex_t fine_grain[HASH_TABLE_CAPACITY];
};
struct hash_table *hash_table_create() {
struct hash_table *hash_table = calloc(1, sizeof(struct hash_table));
for (size_t i = 0; i < HASH_TABLE_CAPACITY; ++i) {
...
hash_table->fine_grain[i] = PTHREAD_MUTEX_INITIALIZER;
}
return hash-table
}
The reason I ask is that the teaching assistant in charge of the project has stated:
> After using a lock, make sure you will destroy it, we will check for memory leaks
This is not referencing the allocated hash table, but the individual locks, so I am wondering why `pthread_mutex_destroy` might ever be necessary.
</details>
# 答案1
**得分**: 2
Sure, here's the translated content:
> 如果不调用pthread_mutex_destroy,并且在什么情况下需要调用它?
>
> 我不想要内存泄漏。
如果只是有一个变量 `pthread_mutex_t a = PTHREAD_MUTEX_INITIALIZER;` 而且 _你什么都不做_,那么调用 `pthread_mutex_destroy` 是不必要的。
如果你在它上面调用 `pthread_mutex_lock(&a)`,锁定函数可能会分配内存或其他资源。在这种情况下,你应该调用 `pthread_mutex_destroy(&a)` 以确保实现释放该内存。
但是是的 - _可能_ 存在不为互斥锁分配内存的实现。这不是限制,可能存在在调用 `_lock()` 时为互斥锁分配内存的实现,因此为了在这种情况下不产生任何泄漏,你应该调用 `_destroy()`。
你链接的POSIX规范有一个专门讨论互斥锁的静态初始化的部分。更新版本可以在 https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_destroy.html 找到。特别是,它提到:
> 但是关于锁定性能的问题可能会在要求从特殊内存分配互斥锁的机器上提出。这些机器实际上必须具有互斥锁,可能还包含指向实际硬件锁的指针的条件变量。为了使静态初始化在这些机器上起作用,pthread_mutex_lock() 还必须测试指向实际锁的指针是否已分配。如果没有,pthread_mutex_lock() 必须在使用之前初始化它。这些资源的保留可以在程序加载时完成,因此没有将返回代码添加到互斥锁定和条件变量等待以指示初始化失败。
>
> pthread_mutex_lock() 中的这个运行时测试起初似乎是额外的工作;需要额外的测试以查看指针是否已初始化。在大多数机器上,这实际上会被实现为对指针的提取,将指针与零进行测试,然后如果已经初始化,则使用指针。虽然这个测试似乎增加了额外的工作,但测试寄存器的额外努力通常是可以忽略不计的,因为实际上不会执行额外的内存引用。随着越来越多的机器提供缓存,真正的开销是内存引用,而不是执行的指令。
<details>
<summary>英文:</summary>
> if it is okay not to call pthread_mutex_destroy and in what circumstances it is necessary?
>
> I do not want memory leaks.
If you have just a variable `pthread_mutex_t a = PTHREAD_MUTEX_INITIALIZER;` and _you do nothing with it_ then calling `pthread_mutex_destroy` is not necessary.
If you call `pthread_mutex_lock(&a)` on it, the lock function can potentially allocate memory or other resources. In such case, you should call `pthread_mutex_destroy(&a)` to make sure the implementation frees that memory.
But yes - there _may_ and do exist implementations that do not allocate memory for mutexes. This it not a restriction, there may exist implementations that do allocate memory for mutexes upon calling `_lock()`, so to not have any leaks on such implementatin you should call `_destroy()`.
The POSIX specification you linked has a whole section discussing static initialization for mutexes. Newer version is available at https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_destroy.html . In particular, it mentions:
> Yet the locking performance question is likely to be raised for
> machines that require mutexes to be allocated out of special memory.
> Such machines actually have to have mutexes and possibly condition
> variables contain pointers to the actual hardware locks. For static
> initialization to work on such machines, pthread_mutex_lock() also has
> to test whether or not the pointer to the actual lock has been
> allocated. If it has not, pthread_mutex_lock() has to initialize it
> before use. The reservation of such resources can be made when the
> program is loaded, and hence return codes have not been added to mutex
> locking and condition variable waiting to indicate failure to complete
> initialization.
>
> This runtime test in pthread_mutex_lock() would at first seem to be
> extra work; an extra test is required to see whether the pointer has
> been initialized. On most machines this would actually be implemented
> as a fetch of the pointer, testing the pointer against zero, and then
> using the pointer if it has already been initialized. While the test
> might seem to add extra work, the extra effort of testing a register
> is usually negligible since no extra memory references are actually
> done. As more and more machines provide caches, the real expenses are
> memory references, not instructions executed.
</details>
# 答案2
**得分**: 1
TLDR:
严格来说,在任何情况下,如果您要确保没有资源泄漏,您需要为每个初始化的互斥量调用`pthread_mutex_destroy()`。
实际上,大多数情况下都不会有关系,要么因为没有办法调用`pthread_mutex_destroy()`,要么因为您知道您的实现不会在互斥量中使用动态内存,要么因为进程正在退出,您并不关心是否会泄漏。
<details>
<summary>英文:</summary>
**TLDR:**
Pedantically, you need to call `pthread_mutex_destroy()` for every initialized mutex if you need to be absolutely certain no resources are leaked under any circumstances.
In reality, most times it won't matter, either because there's no way to call `pthread_mutex_destroy()`, or you know your implementation doesn't use dynamic memory in mutexes, or the process is exiting and you don't really care if it leaks.
**Full Answer:**
To expand on @KamilCuk's answer, from his (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_destroy.html):
> An implementation may cause `pthread_mutex_destroy()` to set the object referenced by mutex to an invalid value.
One way to do that is for an implementation to call `free()` for mutex resources allocated during the creation *and use* of the mutex. Nothing rules out an implementation using dynamic allocation for any mutex - an implementation is even free under the "as-if" rules of C's abstract machine to use a `PTHREAD_MUTEX_INITIALIZER` that performs dynamic allocation even for static initialization of static variables at file scope, where normally C would not allow something like
static some_type *var = malloc( sizeof( *var ) );
(If C++ can create `new` objects at file scope, it'd be almost trivial for a C implementation to use something similar internally, even if it flags *your* attempts to do so as C errors.)
So pedantically, if you want to strictly avoid leaking any memory, `pthread_mutex_destroy()` must be called for *every* mutex you initialize.
The reality is different.
First, many (most?) of the time you will use a mutex, there will be no easy way to destroy it. For example, it can be a `static` with limited scope deep inside a function used to protect a critical section of code - there's no way to reliably call `pthread_mutex_destroy()` on such a mutex. Even if you register an `atexit()` function to destroy the mutex, that function won't be called in all ways a process can exit.
Second, most implementation of standard mutexes **don't** allocate any dynamic memory.
Third, many (most?) uses of mutexes are meant to exist for the life of the program - they only need to be destroyed if the program is exiting. And if the program is exiting, there's no functional impact if they're not destroyed.
But if you're creating `struct`s dynamically and initializing mutexes in the `struct`s with `pthread_mutex_init()`, you really *should* run `pthread_mutex_destroy()` on them when you `free()` the `struct` even if you know your use of your implementation's mutex doesn't do any dynamic allocation - someone might change the mutex to something more complex like an error-checking or robust mutex that might very well dynamically allocate memory on your implemenation, the code could be ported to another implementation, or the implementation itself could change. Get it right and don't open up that window for a completely unnecessary future bug.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论