一次性初始化

一些 C 库是为动态初始化设计的,这些库的全局初始化是在调用库中的第一个过程时执行的。

在单线程程序中,这通常通过使用静态变量(在每个例程的入口检查其值)实现,如下面的代码片段所示:

static int isInitialized = 0;
extern void Initialize();

int function()
{
        if (isInitialized == 0) {
                Initialize();
                isInitialized = 1;
        }
        ...
}

对于多线程程序中的动态库初始化,单个初始化标记是不够的。 必须保护此标记不被同时调用库函数的多个线程更改。 保护标记需要使用互斥对象;但是在使用互斥对象之前必须将其初始化。 确保互斥对象仅被初始化一次需要递归解决方案。

要在多线程程序中保持相同的结构,请使用 pthread_once 子例程。 否则,库初始化必须通过在使用库之前显式调用库导出初始化函数来完成。 pthread_once 子例程还提供初始化互斥对象和条件变量的其他方法。

请阅读下面的内容,了解更多有关一次性初始化的信息:

一次性初始化对象

初始化的唯一性通过一次性初始化对象确保。 此变量的数据类型为 pthread_once_t。 在AIX和线程库的大多数其他实现中,pthread_once_t数据类型是一个结构体。

一次性初始化对象是典型的全局变量。 必须使用 PTHREAD_ONCE_INIT 宏对其进行初始化,如以下示例中所示:

static pthread_once_t once_block = PTHREAD_ONCE_INIT;

初始化还能在初始线程或者其他任何线程中执行。 可在同一个程序中使用多个一次性初始化对象。 唯一的要求是一次性初始化对象必须使用宏初始化。

一次性初始化例程

如果是第一次被调用,pthread_once 子例程调用与指定的一次性初始化对象相关的指定初始化例程;否则,它不进行任何操作。 相同的初始化例程始终必须与相同的一次性初始化对象一起使用。 初始化例程必须有以下原型:
void init_routine();

pthread_once 子例程不提供取消点。 但是,初始化例程可能提供取消点,如果启用了可取消,那么调用 pthread_once 子例程的第一个线程可能在执行初始化例程期间被取消。 在这种情况下,并不认为该例程被执行,下一次调用 pthread_once 子例程将会重新调用初始化例程。

建议在一次性初始化例程中使用清除处理程序,特别是执行非 idempotent 操作时,例如,打开文件、锁定互斥对象或者分配内存。

一次性初始化例程可用于初始化互斥对象或者条件变量或者执行动态初始化。 在多线程库中,上面显示的代码片段 (void init_routine();) 将按如下所示编写:
static pthread_once_t once_block = PTHREAD_ONCE_INIT;
extern void Initialize();

int function()
{
        pthread_once(&once_block, Initialize);
        ...
}