(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:只在第一个第一个线程进入时执行一次
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

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