(P38)posix線程

1.線程屬性

  • 初始化與銷燬屬性
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
  • 獲取與設置分離屬性
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);

設置爲JOINABLE,等待線程退出,不至於線程成爲殭屍狀態
設置爲DETACHED,即使線程退出,調用該線程的函數沒有調用pthread_join,該線程也不會處於殭屍狀態
  • 獲取與設置棧大小
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);

stacksize默認爲0,設置不好,會導致一致性問題
  • 獲取與設置棧溢出保護區大小
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize);
  • 獲取與設置線程競爭範圍
int pthread_attr_setscope(pthread_attr_t *attr, int scope);
int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);
  • 獲取與設置調度策略
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
  • 獲取與設置繼承的調度策略
int pthread_attr_setinheritsched(pthread_attr_t *attr,
                                        int inheritsched);
int pthread_attr_getinheritsched(const pthread_attr_t *attr,
                                        int *inheritsched);
  • 獲取與設置調度參數:只關心調度優先級
int pthread_attr_setschedparam(pthread_attr_t *attr,
                                      const struct sched_param *param);
int pthread_attr_getschedparam(const pthread_attr_t *attr,
                                      struct sched_param *param);
  • 併發級別:獲取與設置併發級別
    解釋:默認爲0,表示內核以自己認爲合適的方式進行映射
int pthread_attr_setconcurrency(int new_level);
int pthread_attr_setconcurrency(void);

注意:僅在N:M線程模型中有效,設置併發級別,僅給內核一個提示:表示給定級別數量的核心線程來映射用戶線程是高效的。
  • 併發級別進一步解釋:當前用戶線程爲3個,給定三個核心線程是高效的(內核),但是多個用戶線程對應同一個輕量級進程LWP,這一個輕量級進程又對應一個核心線程,此時併發量不等於5,而後三者是共享一個併發的,所以說只是給內核一個提示,告訴內核以這種映射方式是高效的
    在這裏插入圖片描述

  • eg:NetworkProgramming-master (1)\NetworkProgramming-master\P37pthreadattr.c

#include <stdio.h>
#include <errno.h>
#include <string.h>

// written by wangji
#define ERR_EXIT(m) \
        do  \
        {   \
            perror(m);  \
            exit(EXIT_FAILURE); \
        } while(0);



int main(void)
{
    pthread_attr_t attr;
    pthread_attr_init(&attr);//默認,擁有很多種屬性的默認值

    int state;
    pthread_attr_getdetachstate(&attr,&state);//線程的分離屬性的默認值保存在state
    if (state == PTHREAD_CREAT_JOINABLE)
        printf("detachstate:PTHREAD_CREAT_JOINABLE");
    else if (state == PTHREAD_CREAT_DETACHED)
        printf("detachstate:PTHREAD_CREAT_DETACHED");

    size_t size;
    pthread_attr_getstachsize(&attr, &size);
    printf("stacksize:%d\n", size);

    pthread_attr_getguardsize(&attr, &size);
    printf("guardsize:size\n",size);


    int scope;
    pthread_attr_getscope(&atte, &scope);
    if (scope == PTHREAD_SCOPE_PROCESS)//線程的競爭範圍在進程內
        printf("scope:PTHREAD_SCOPE_PROCESS\n");
    if (  scope == PTHREAD_SCOPE_SYSTEM )//線程的競爭範圍在系統範圍內
        printf("scope:PTHREAD_SCOPE_SYSTEM\n");

    //線程的調度策略
    int policy;
    pthread_attr_getschedpolicy(&attr,&policy);
    if ( policy == SCHED_FIFO)
        printf("policy:SCHED_FIFO\n");//若線程優先級相同,按照陷入先出的方式調度
    else if ( policy == SCHED_RR)
        printf("policy:SCHED_RR\n");//即使線程優先級相同,搶佔式調度
    else if ( policy == SCHED_OTHER)
        printf("policy:SCHED_OTHER\n");

    int inheritsched;
    pthread_attr_getinheritsched(&attr, &inheritsched);
    if ( inheritsched == PTHREAD_INHERIT_SCHED)//新創建的線程將繼承調用者線程的調度策略屬性
        printf("inheritsched:PTHREAD_INHERIT_SCHED\n");
    else if ( inheritsched == PTHREAD_EXPLICIT_SCHED )//表示新創建的線程屬性需要自己設置,由setschedpolicy設置
        printf("inheritsched:PTHREAD_EXPLICIT_SCHED\n");

    struct sched_param param;
    pthread_attr_getschedparam(&attr, &param);
    printf("sched priority:%d\n", param.sched_priority);

    pthread_attr_destroy(&attr);

    int level;
    level = pthread_getconcurrency();//併發級別
    printf("level:%d\n", level);


    return 0;
}
  • 測試結果:
    在這裏插入圖片描述

2.線程特定數據

  • 在單線程程序中,我們經常要用到全局變量以實現多個函數間共享數據

  • 在多線程環境下,由於數據空間是共享的,因此全局變量也爲所有線程所公有

  • 但有時應用程序中有必要提供線程私有的全局變量,僅僅在某個線程中有效,但卻可以跨多個函數訪問

  • POSIX線程庫通過維護一定的數據結構來解決這個問題,這些個數據稱之爲(Thread-specific Data,或者TSD)

  • 示例圖如下:
    (1)每個線程都有特定數據128項,也就是都有128個key,以key->value形式來組織的。
    (2)若線程0創建key1,則其它線程也會得到key1。相當於兩個線程都具有a的全局變量。但是每個線程都指向自己的數據a,並不是指向同一個內存。
    (3)線程0對a改變,並不會影響到線程n
    在這裏插入圖片描述

  • 線程特定數據的函數

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

void *pthread_getspecific(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);


pthread_once:只在第一個第一個線程進入時執行一次
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
pthread_once_t once_control=PTHREAD_ONCE_INIT;
  • eg1:NetworkProgramming-master (1)\NetworkProgramming-master\P37thread.c
//
// Created by wangji on 19-8-14.
//

// p37 poxis 線程(二)

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

using namespace std;


#define ERR_EXIT(m) \
        do  \
        {   \
            perror(m);  \
            exit(EXIT_FAILURE); \
        } while(0);

struct tsd
{
    pthread_t id;
    char* arg;
}tsd_t;

pthread_key_t thread_key;//全局的TSD數據key

pthread_once_t once = PTHREAD_ONCE_INIT;

void destr_function(void *)
{
    printf("destroy...\n");
    free(value);
}

void once_run(void)
{
    pthread_key_create(&thread_key, destr_function);
    // cout<<"once_run in thread "<<(unsigned int )pthread_self()<<endl;
    printf("key init ... \n");
}

void * start_routine (void *arg)
{
    // pthread_once(&once, once_run);//只在第一個第一個線程進入時執行once_run,意味着key只創建1次
    tsd_t *value = (tsd_t*)malloc(sizeof(tsd_t));
    value->arg = (char *)arg;
    value->id = pthread_self();tsd_t

    //給線程設定特定數據
    pthread_setspecific(thread_key, value);
    printf("%s setspecific %p\n", (char*)arg, value);

    value = (struct tsd_t*)pthread_getspecific(thread_key);
    printf("tid = 0x%x str = %s\n", (int)value->id, value->arg);//打印線程ID和str
    sleep(2);
    value = (struct tsd_t*)pthread_getspecific(thread_key);
    printf("tid = 0x%x str = %s\n", (int)value->id, value->arg);

    return NULL;
}



int main(int argc, char** argv) {

    //thread_key:key是每個線程的全局變量,但是key指向的變量是每個線程所獨享的
    //每個線程都有thread_key變量
    //destr_function主要是用來銷燬key所指向的value的數據的
    //當下面2個線程退出時,都會執行destr_function銷燬數據,即執行2次
    pthread_key_create(&thread_key, destr_function);

    pthread_t thread1;
    pthread_t thread2;

    pthread_create(&thread1, NULL, start_routine, (char *)"thread1");
    pthread_create(&thread2, NULL, start_routine, (char *)"thread2");


    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    //當兩個線程退出時,刪除key
    pthread_key_delete(thread_key);
    return 0;
}
  • 測試結果:
    在這裏插入圖片描述

  • eg2:NetworkProgramming-master (1)\NetworkProgramming-master\P37thread.c
    使用pthread_once

//
// Created by wangji on 19-8-14.
//

// p37 poxis 線程(二)

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

using namespace std;


#define ERR_EXIT(m) \
        do  \
        {   \
            perror(m);  \
            exit(EXIT_FAILURE); \
        } while(0);

struct tsd
{
    pthread_t id;
    char* arg;
}tsd_t;

pthread_key_t thread_key;//全局的TSD數據key

pthread_once_t once = PTHREAD_ONCE_INIT;

void destr_function(void *)
{
    printf("destroy...\n");
    free(value);
}

void once_run(void)
{
    pthread_key_create(&thread_key, destr_function);
    // cout<<"once_run in thread "<<(unsigned int )pthread_self()<<endl;
    printf("key init ... \n");
}

void * start_routine (void *arg)
{
     pthread_once(&once, once_run);//只在第一個第一個線程進入時執行once_run,意味着key只創建1次
    tsd_t *value = (tsd_t*)malloc(sizeof(tsd_t));
    value->arg = (char *)arg;
    value->id = pthread_self();tsd_t

    //給線程設定特定數據
    pthread_setspecific(thread_key, value);
    printf("%s setspecific %p\n", (char*)arg, value);

    value = (struct tsd_t*)pthread_getspecific(thread_key);
    printf("tid = 0x%x str = %s\n", (int)value->id, value->arg);//打印線程ID和str
    sleep(2);
    value = (struct tsd_t*)pthread_getspecific(thread_key);
    printf("tid = 0x%x str = %s\n", (int)value->id, value->arg);

    return NULL;
}



int main(int argc, char** argv) {

    //thread_key:key是每個線程的全局變量,但是key指向的變量是每個線程所獨享的
    //每個線程都有thread_key變量
    //destr_function主要是用來銷燬key所指向的value的數據的
    //當下面2個線程退出時,都會執行destr_function銷燬數據,即執行2次
    // pthread_key_create(&thread_key, destr_function);

    pthread_t thread1;
    pthread_t thread2;

    pthread_create(&thread1, NULL, start_routine, (char *)"thread1");
    pthread_create(&thread2, NULL, start_routine, (char *)"thread2");


    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    //當兩個線程退出時,刪除key
    pthread_key_delete(thread_key);
    return 0;
}
  • 測試結果:
    在這裏插入圖片描述
  • Makefile文件
.PHONY=clean all
CFLAGS=-Wall -g
CC=gcc
BIN=threadattr tsd
all:$(BIN)
%.o:%c
	$(CC) $(CFLAGS) -c $< -o $@
threadattr:threadattr.o
	$(CC) $(CFLAGS) $^ -o $@ -lpthread
tsd:tsd.o
	$(CC) $(CFLAGS) $^ -o $@ -lpthread
clean:
	rm -f $(BIN) *.o

在這裏插入圖片描述
設置爲JOINABLE,等待線程退出,不至於線程成爲殭屍狀態
設置爲DETACHED,即使線程退出,調用該線程的函數沒有調用pthread_join,該線程也不會處於殭屍狀態
在這裏插入圖片描述
stacksize默認爲0,設置不好,會導致一致性問題
在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

只關心調度優先級
在這裏插入圖片描述

默認爲0,表示內核以自己認爲合適的方式進行映射
表示:當前用戶線程爲3個,給定三個核心線程是高效的(內核),但是多個用戶線程對應同一個輕量級進程LWP,這一個輕量級進程又對應一個核心線程,此時併發量不等於5,而後三者是共享一個併發的,所以說只是給內核一個提示,告訴內核以這種映射方式是高效的
在這裏插入圖片描述

在這裏插入圖片描述
每個線程都有特定數據128項,也就是都有128個key,以key->value形式來組織的。
若線程0創建key1,則其它線程也會得到key1。相當於兩個線程都具有a的全局變量。但是每個線程都指向自己的數據a,並不是指向同一個內存。
線程0對a改變,並不會影響到線程n
在這裏插入圖片描述
once:只在第一個第一個線程進入時執行一次
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章