1

我有一个必须在线程之间共享的数组,受信号量保护。我把初始化代码放在一个可以多次调用的函数中,一个“构造函数”,如下:

#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?),或者我是否应该将初始化代码与构造函数分开并在没有并发的情况下在主线程中使用它。

这段代码正在使用中,我还没有遇到竞争状况。但是,据我所知,这是可能的,我希望纠正它。

4

2 回答 2

2

如果信号量的真正用途是作为互斥体,那么就使用pthread_mutex_t. 这些可以静态初始化,这样你的问题就会消失。

语法是

pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;

如果您确实需要全局对象的动态初始化,请查看pthread_once. pthread_once_t这是POSIX 为此类任务预见的类型 ( ) 和功能。

于 2012-09-17T11:57:43.330 回答
1

有几种方法可以进行线程安全的延迟初始化,但这不是其中之一。

pthread_once是一种方式,而使用实际上是互斥体(静态初始化)的全局互斥体来同步初始化是另一种方式。实现可以保证局部变量的线程安全初始化static,但不是必须的(至少,它们在 C11 之前没有,我还没有检查过)。

但是,您可以同步实际的初始化,但不能保证双重检查锁定在 C 或 Posix 中有效检查一个线程中的标志是一场数据竞赛,该标志是在另一个线程中设置的,而两个线程中没有某种同步。pthread_once在初始化已经完成的常见情况下,应该尽可能快地实现。如果您的实现保证函数范围的静态变量的线程安全初始化,那么这也将尽力而为。除非您真的知道自己在做什么(例如,您正在pthread_once为某个新系统实现自己),否则请使用其中之一,而不是自己尝试避免在常见情况下代价高昂的锁定。

于 2012-09-17T12:06:50.307 回答