1. 程式人生 > >Linux學習之多執行緒程式設計(六)

Linux學習之多執行緒程式設計(六)

言之者無罪,聞之者足以戒。 ——《詩序》

4、執行緒私有屬性

應用程式設計中有必要提供一種變數,使得多個函式多個執行緒都可以訪問這個變數(看起來是個全域性變數),但是執行緒對這個變數的訪問都不會彼此產生影響(貌似不是全域性變數哦),但是你需要這樣的資料,比如errno。那麼這種資料就是執行緒的私有資料,儘管名字相同,但是每個執行緒訪問的都是資料的副本。

在使用私有資料之前,你首先要建立一個與私有資料相關的鍵,要來獲取對私有資料的訪問許可權 。這個鍵的型別是pthread_key_t

(1)、pthread_key_create建立私有資料的鍵

int pthread_key_create(pthread_key_t *key, void (*destructor)(void*))

第一個引數:建立一個鍵值

第二個引數:解構函式

返回值:成功返回0,失敗返回錯誤碼

建立的鍵放在key指向的記憶體單元,destructor是與鍵相關的解構函式。當執行緒呼叫pthread_exit或者使用return返回,解構函式就會被呼叫。當解構函式呼叫的時候,它只有一個引數,這個引數是與key關聯的那個資料的地址(也就是你的私有資料啦),因此你可以在解構函式中將這個資料銷燬。

鍵使用完之後也可以銷燬,當鍵銷燬之後,與它關聯的資料並沒有銷燬哦

(2)、pthread_key_delete銷燬私有資料的鍵

int pthread_key_delete(pthread_key_t key)

引數:要銷燬的鍵

返回值:成功返回0,失敗返回錯誤碼

有了鍵之後,你就可以將私有資料和鍵關聯起來,這樣就就可以通過鍵來找到資料。所有的執行緒都可以訪問這個鍵,但他們可以為鍵關聯不同的資料。(這豈不是一個名字一樣,而值卻不同的全域性變數麼)

(3)、pthread_setspecific私有資料與鍵值關聯函式

int pthread_setspecific(pthread_key_t key, const void *value)

第一個引數:要關聯的鍵

第二個引數:關聯的資料

返回值 :成功返回0,失敗返回錯誤碼

(4)、*pthread_getspecific獲取私有資料的地址

void *pthread_getspecific(pthread_key_t key)

引數:關聯的鍵值

返回值:有資料關聯返回資料值,沒有資料關聯返回NULL

下面看一下程式程式碼:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>


pthread_key_t key;

void *thread_fun1(void *arg)
{
        printf("xiaoyi one\n");
        int a=6;
        //將key與a關聯
        pthread_setspecific(key,(void *)a);
        sleep(2);
        printf("xiaoyi one key data is %d\n",pthread_getspecific(key));
}
void *thread_fun2(void *arg)
{
        sleep(1);
        printf("xiaoyi two\n");
        int a=10;
        //將key與a關聯
        pthread_setspecific(key,(void *)a);
        printf("xiaoyi two key data is %d\n",pthread_getspecific(key));
}

int main()
{
        pthread_t tid1,tid2;
        //創造一個key
        pthread_key_create(&key,NULL);

        //創造新的執行緒
        if(pthread_create(&tid1,NULL,thread_fun1,NULL))
        {
                printf("create new thread failure\n");
                return ;
        }

        if(pthread_create(&tid2,NULL,thread_fun2,NULL))
        {
                printf("create new thread failure\n");
                return ;
        }
        //等待執行緒的結束
        pthread_join(tid1,NULL);
        pthread_join(tid2,NULL);

        return 0;
}

5、執行緒與fork

命令:ps 檢視程序

命令:kii 程序inode號   (殺死一個死執行緒)

當執行緒呼叫fork函式時,就為子程序建立了整個程序地址空間的副本,子程序通過繼承整個地址空間的副本,也會將父程序的互斥量、讀寫鎖、條件變數的狀態繼承過來。也就是說,如果父程序中互斥量是鎖著的,那麼在子程序中互斥量也是鎖著的(儘管子程序自己還沒有來得及lock),這是非常不安全的,因為不是子程序自己鎖住的,它無法解鎖。

子程序內部只有一個執行緒,由父程序中呼叫fork函式的執行緒副本構成。如果呼叫fork的執行緒將互斥量鎖住,那麼子程序會拷貝一個pthread_mutex_lock副本,這樣子程序就有機會去解鎖了。或者互斥量根本就沒被加鎖,這樣也是可以的,但是你不能確保永遠是這樣的情況。

pthread_atfork函式給你創造了這樣的條件,它會註冊三個函式

(1)、pthread_atfork

int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))

第一個引數:獲取鎖狀態的函式

第二個引數:fork建立的父程序

第三個引數:fork建立的子程序

返回值:成功 返回0,失敗返回錯誤碼

prepare是在fork呼叫之前會被呼叫的,parent在fork返回父程序之前呼叫,child在fork返回子程序之前呼叫。如果在prepare中加鎖所有的互斥量,在parent和child中解鎖所有的互斥量,那麼在fork返回之後,互斥量的狀態就是未加鎖。

可以有多個 pthread_atfork函式,這時也就會有多個處理程式(prepare,parent,child)。多個prepare的執行順序與註冊順序相反,而parent和child的執行順序與註冊順序相同。

下面來看一下程式程式碼:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

void prepare()
{
        pthread_mutex_lock(&mutex);
        printf("I am prepare\n");
}
void parent()
{
        pthread_mutex_unlock(&mutex);
        printf("I am parent\n");
}
void child()
{
        pthread_mutex_unlock(&mutex);
        printf("I am child\n");
}
void *thread_fun(void *arg)
{
        sleep(1);
        pid_t pid;
        pthread_atfork(prepare,parent,child);
        pid=fork();
        if(pid == 0)
        {
                pthread_mutex_lock(&mutex);
                printf("child process\n");
                pthread_mutex_unlock(&mutex);
        }
        if(pid > 0)
        {
                pthread_mutex_lock(&mutex);
                printf("parent process\n");
                pthread_mutex_unlock(&mutex);
        }
}

int main()
{
        pthread_t tid;
        if(pthread_create(&tid,NULL,thread_fun,NULL))
        {
                printf("create new thread failure\n");
                return ;
        }

        pthread_mutex_lock(&mutex);
        sleep(1);
        printf("main process\n");
        pthread_mutex_unlock(&mutex);
        pthread_join(tid,NULL);

        pthread_mutex_destroy(&mutex);
        return 0;
}