【发布时间】:2012-09-17 11:52:07
【问题描述】:
我有一个必须在线程之间共享的数组,受信号量保护。我把初始化代码放在了一个可以多次调用的函数中,一个“构造函数”,如下:
#include <stdbool.h> //for bool
#include <semaphore.h>
sem_t global_mutex;
char global_array[N]; // Protected with global_mutex
struct my_struct *new_my_struct(){
static bool is_init = false; // This will be initialized only once, right?
if (!is_init){ // 1
sem_init(&global_mutex, 0, 1); // 2
sem_wait(&global_mutex); // 3
if (!is_init){ // 4
is_init = true; // 5
... initialize global_array ... // 6
}
sem_post(&global_mutex); // 7
}
... proceed on the create and return a my_struct pointer ...
}
在理想情况下,线程将从 1 运行到 7,初始化数组并退出临界区。即使另一个线程在 2 中停止,在 4 中的测试也会是错误的,并且不会覆盖数组。我没有想过如果线程卡在 1 并重新初始化信号量会发生什么,但我相信只要is_init 由第一个运行的线程设置为 true 就不会引起太大的关注!
现在,如果一个线程在 4 处停止,另一个线程从开始运行到完成,初始化并填充 global_array,则存在竞争条件。当线程停止在 4 运行时,它会重新初始化数组并删除第一个线程存储的状态。
我想知道是否有任何方法可以避免这种竞争条件(也许巧妙地使用static?)或者我是否应该将初始化代码与构造函数分开并在主线程中使用它,当没有并发。
此代码正在使用中,我还没有遇到竞争状况。但是,据我所知,这是可能的,我希望更正它。
【问题讨论】:
-
由于数组是全局的,为什么不初始化它和信号量before你开始你的线程?那么你就不用担心初始化代码中的竞争条件了。
-
真的有必要检查 (!is_init) 两次吗? ;o 是的,静态布尔值将被视为函数范围内的全局变量。
-
@Magtheridon96 是的,一个线程可以在 3 处停止,另一个线程继续到 5。然后第一个线程不会进入 if,第二个线程将继续初始化数组。糟糕,您刚刚发现了一个错误!第一个线程可以继续使用未初始化数组的函数!
-
@JoachimPileborg 出于美学和易用性考虑,我不希望这样做。您不会看到许多标准库要求您在使用其他功能之前在主线程中调用初始化函数,并且不要求用户记住这样做更安全。
-
我很难想到任何没有具有某种打开、创建或初始化功能的库。对于一些你甚至必须调用一个 init 函数之前你可以调用 open 函数(例如 MySQL)。
标签: c concurrency semaphore