多線程如何優雅地初始化全局變量?

需求場景

若是使用多線程,那麼幾乎都會用到全局變量,這時初始化全局變量的技巧就很重要了。linux

一般初始化全局變量時就是像下面這樣的,先判斷是否已經初始化過了,而後纔去初始化。在單線程場景下,lazy初始化(就是用到時才初始化)通常是下面這樣寫的,這沒問題。可是多線程場景下就不能這樣寫了,咱們要先給random_is_initialized建立一個mutex,不然這段代碼就問題大了。可是mutex也得初始化吧?初始化又要建立一個mutex來保證前一個mutex能正常初始化,這就陷入死循環了。git

static int random_is_initialized = 0;
extern int initialize_random(); // 這個函數用來初始化全局變量

int random_function()
{
    if (random_is_initialized == 0) {
        initialize_random();
        random_is_initialized = 1;
    }
    ... /* Operations performed after initialization. */
}

POSIX提供了一個函數pthread_once,很適合解決這種問題。它能保證只初始化一次全局變量,並且線程安全,開發起來就很方便了。使用方法參考下面的實現。安全

實現

#include <pthread.h>

static pthread_once_t random_is_initialized = PTHREAD_ONCE_INIT;
void initialize_random() 
{
    printf("this will be printed only once\n");
}

void *random_function(void *none)
{
    (void) pthread_once(&random_is_initialized, initialize_random);
    // xxx邏輯代碼
    return NULL;
}

int main()
{
    int ret = 0;
    pthread_t thread;

    //建立10個線程
    for (int i = 0; i < 10; i++) {
        pthread_create(&thread, NULL, random_function, NULL);
    }
    sleep(100000);  // 主線程不能退出
    return 0;
}

編譯 gcc -o test test.cpp -lpthread多線程

你可能有疑問,爲何不能在建立線程以前就初始化全局變量?那樣的話不須要考慮什麼線程安全。確實是的。其餘場景可能有更好的發揮餘地。dom

注意

使用pthread_once確定會好奇它是怎麼實現的,它的glibc實如今這裏。其實就是首個線程執行到pthread_once這裏了,其餘線程就得等,直到首個線程執行完以後去喚醒其餘線程。函數

回調函數initialize_random中不該該有耗時的操做,一不當心可能永遠不會返回,這時其餘的線程就會一直睡眠,這個進程就廢了。this

參考

  • https://linux.die.net/man/3/pthread_once
相關文章
相關標籤/搜索