Unix_Linux操作系统-笔记Day10(线程)


Day10

线程

线程基本概念

  • 线程的就是进程中的执行路线,即进程内部的控制序列,或者是进程的子任务(进程就是正在运行的程序,它是一个资源单位)
  • 线程就是轻量级的,没有自己独立的内存资源,使用的是进程的代码段,数据段,bss段,堆(没有栈),环境变量表,命令行参数,文件描述符,信号处理函数,工作目录,用户ID,组ID等资源
  • 线程拥有自己独立的栈,也就是由自己独立的局部变量
  • 一个进程中可以同时拥有多个线程,即同时被系统调度的多条执行路线,但至少有一个主线程,当主线程结束,子线程全部结束

线程的基本特点

  • 线程是进程的实体,可以作为系统独立到调度和分配基本单位
  • 线程有不同的状态,系统提供了多种线程控制的原语(控制方法(创建线程,销毁线程))
  • 线程不拥有自己的资源(唯一拥有的就是自己的栈空间),只拥有从从属于进程的全部资源,所有资源分配都是面向进程的
  • 一个进程中可以有多个线程同时执行,它们可以执行相同的代码,也可以执行不同的代码
  • 同一进程内的线程都在同一个地址空间下活动(0~4G),相比较于多进程,多线程的系统开销小,任务切换快
  • 多进程协同工作时需要通信,而多线程之间的数据交换不需要依赖IPC的特殊通信机制,简单而高效
  • 每个线程拥有自己独立的线程ID,寄存器信息,函数,栈等
  • 线程之间也存在优先级

注意 进程与线程的区别?

POSIX线程

  • 早期的UNIX操作系统是没有线程的,而是各计算机厂商提供了自己私有的线程库,不易于移植
  • 在1995年左右,定义了同一的线程编程接口POXIS线程,即pthread
  • pthread包含一个头文件pthread.h,一个库libpthread.so
  • 功能
    • 线程管理: 创建/销毁,分离/联合,设置/获取属性
    • 线程同步(互斥):互斥量(互斥锁),条件变量,信号量

线程函数

  • int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
    • 创建线程
    • thread 获取线程ID
    • attr 创建线程时所需要的属性设置,如果为空,按照默认方式创建线程
    • start_routine 线程的入口函数
    • arg 给线程入口函数传递的参数
#include 
#include 
#include 
void* start_run(void* arg){
    printf("---%s---\n",(char*)arg);
    for(;;){
        printf("*");
        fflush(stdout);
        sleep(1);
    }
}

int main(){
    char* str = "hhhh";
    pthread_t pid;
    pthread_create(&pid,NULL,start_run,str);
    for(;;){
        printf("#");
        sleep(1);
    }


}

gcc a.c -lpthraed

练习1 把TCP S端的多进程改成多线程

Windows下:

#include 
#include 
#include 
DWORD start_run(void* arg){
    printf("---%s---\n",(char*)arg);
    for(;;){
        printf("*");
        fflush(stdout);
        sleep(1);
    }
}

int main(){
    char* str = "hhhh";
    pthread_t pid;
    CreateThread(&pid,NULL,start_run,str);
    for(;;){
        printf("#");
        sleep(1);
    }


}

  • int pthread_join(pthread_t thread, void **retval);
    • 等待线程结束并获取线程入口函数的返回值,线程结束时函数才返回
    • thread 线程ID
    • retval 指针变量的地址,用于获取线程入口函数的返回值
    • 注意 线程入口函数在返回数据时,不能返回指向私有栈空间的指针,如果获取到的是指向堆的指针,等待着要负责把该空间释放

void* start_run(void* arg){
    char str[] = "haha";    //无法返回,局部变量
    char* str = "hhh";      //可以返回,代码段
    return str;
}

int main(){
    pthread_t pid;
    pthread_create(&pid,NULL,start_run,NULL);
    void* ptr = NULL;
    pthread_join(pid,&ptr);
    printf("%p,%s\n",ptr,(char*)ptr);
}
  • pthread_t pthread_self(void);

    • 返回当前线程的ID
  • int pthread_equal(pthread_t t1, pthread_t t2);

    • 比较两个线程ID
    • 返回 0 两个线程ID是同一个线程 否则 -1
    • 注意 pthread_t 不一定是lu,有些系统是结构体类型,所以无法使用 == 比较
  • void pthread_exit(void *retval);

    • 调用者线程结束(从入口函数return)
    • retval 会返回给pthread_join函数的第二个参数
    • 注意 如果是进程的最后一个线程,当调用pthread_exit时,进程也就结束了
  • int pthread_detach(pthread_t thread);

    • 使调用线程与线程ID为thread线程称为分离状态
      • 非分离 线程可以被创建者pthread_join等待(回收资源)
      • 分离 线程不需要创建者等待,结束后自动释放资源
  • int pthread_cancel(pthread_t thread);

    • 向指定的线程发送取消操作
    • 注意 对方不一定响应
  • int pthread_setcancelstate(int state, int *oldstate);

    • 设置调用者线程是否响应取消操作
    • state
      • PTHREAD_CANCEL_ENABLE 允许响应
      • PTHREAD_CANCEL_DISABLE 禁止响应
    • oldstate 获取旧的取消状态
  • 线程属性

union pthread_attr_t
{
  char __size[__SIZEOF_PTHREAD_ATTR_T];
  long int __align;
};

typedef struct
{
       int                  __detachstate;   线程的分离状态
       int                  __schedpolicy;  线程调度策略
       struct sched_param   __schedparam;  线程的调度参数
       int                  __inheritsched;  线程的继承性
       int                  __scope;       线程的作用域
       size_t               __guardsize;   
       int                  __stackaddr_set;
       void *               __stackaddr;   线程堆栈的位置
       size_t               __stacksize;    线程栈的大小
}pthread_attr_t;

/*之所以出现和两种不同的定义可能是因为pthread不想让用户空间看到它内部对pthread_attr_t的实现,因为API设计时就封装了这个类型。所以它只要提供和内部实现的pthread_attr_t大小一样的类型就可以了。

实际上,pthread内部对pthread_attr_ t的实现是
struct pthread_attr,它的内容和书上的内容基本上是一致的。同时在机子上测试可以看到这两个结构体的大小是相等的。*/
  • 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);

    • 设置线程属性中分离标志
      • PTHREAD_CREATE_DETACHED 分离
      • PTHREAD_CREATE_JOINABLE 不分离
  • int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);

    • 获取线程属性中分离标志
  • int pthread_attr_setscope(pthread_attr_t *attr, int scope);

    • 设置线程属性中线程的竞争范围
      • PTHREAD_SCOPE_SYSTEM
      • PTHREAD_SCOPE_PROCESS
  • int pthread_attr_getscope(const pthread_attr_t *attr, int *scope);

    • 获取线程属性中线程的竞争范围
  • int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);

    • 设置线程属性中线程的调度策略的来源
    • inheritsched
      • PTHREAD_INHERIT_SCHED 继承创建者
      • PTHREAD_EXPLICIT_SCHED 单独设置
  • int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched);

  • int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);

    • 设置线程属性中线程的调度策略
      • SCHED_FIFO 先进先出
      • SCHED_RR 轮转策略
      • SCHED_OTHER 缺省
  • int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);

    • 设置线程属性中线程的调度策略
  • int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);

    • 最高级别0
  • int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);

    • 获取线程属性中线程的调度参数(优先级别)
  • 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_setstackaddr(pthread_attr_t *attr, void *stackaddr);

    • 设置线程属性中线程的栈底地址
  • int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr);

    • 获取线程属性中栈底地址
  • int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

    • 设置线程属性中线程的栈空间字节数
  • int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);

    • 设置线程属性中线程的栈空间字节数
  • int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);

  • int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize);

使用方法

  1. 定义线程属性结构体
    pthread_attr_t attr
  2. 初始化线程属性结构体
    pthread_attr_init(&attr);
  3. 使用pthread_attr_set系列函数对结构体变量进行设置
  4. 在创建线程时(pthread_create函数的第二个参数)中使用线程属性结构变量创建线程
  • int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr);
    • 获取指定线程的属性

你可能感兴趣的:(笔记)