• 多线程学习笔记


    1. 线程的概念

    1.1 什么是线程

    轻量级进程,本质仍然是进程,都是通过clone函数实现的。
    进程:独立的地址空间,拥有PCB,最小的资源分配单元
    线程:没有独立的地址空间,拥有PCB,最小的执行单元

    1.2 线程共享资源

    • 文件描述符表
    • 每种信号的处理方式
    • 当前工作目录
    • 用户ID和组ID
    • 内存地址空间

    1.3 线程非共享资源

    • 线程id
    • 处理现场和栈指针
    • 独立的栈空间
    • error变量
    • 信号屏蔽字
    • 调度优先级

    1.4 线程优缺点

    优点:提高并发性、占用资源小、通信方便
    缺点:库函数不稳定、编写和调试困难、对信号支持不好

    2. 线程的操作函数

    2.1 pthread_create函数

    #include 
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                       void *(*start_routine) (void *), void *arg);
    
    • 1
    • 2
    • 3

    功能:创建线程
    函数参数:

    • pthread_t *thread 线程ID,传出参数
    • const pthread_attr_t *attr 线程的属性信息,一般用不到的
    • void *(*start_routine) (void *) 线程函数,是一个void *func(void *)形式的函数指针
    • void *arg 函数执行的参数
    • 成功返回0,失败返回errno

    注:编译需要添加pthread

    2.2 phtread_self函数

    #include 
    pthread_t pthread_self(void);
    
    • 1
    • 2

    功能:返回调用函数的ID,这个值与调用pthread_create返回的pthread_t *thread一样。

    2.3 pthread_exit函数

     #include 
     void pthread_exit(void *retval);
    
    • 1
    • 2

    功能:终止调用的进程函数
    函数参数:

    • void *retval:指向的数据将作为线程退出时的返回值,主线程中的 pthread_join() 函数都可以接收到线程的返回值。如果线程不需要返回任何数据,将 retval 参数置为 NULL 即可。

    pthread_exit和return的区别:

    1. pthread_exit() 可以自动调用线程清理程序(本质是一个由 pthread_cleanup_push() 指定的自定义函数),return 则不具备这个能力。
    2. 在主线程中, return 语句不仅会终止主线程执行,还会终止其它子线程执行。pthread_exit() 函数只会终止当前线程,不会影响进程中其它线程的执行。

    2.4 pthread_join函数

    #include 
    int pthread_join(pthread_t thread, void **retval);
    
    
    • 1
    • 2
    • 3

    功能:获取某个线程执行结束时返回的数据,pthread_join会一直阻塞调用它的线程,接收到目标线程的返回值,阻塞状态才会解除。
    参数:

    • thread代表创建的时候传出的第一个参数
    • retval代表传出线程退出的信息

    注:一个线程执行结束的返回值只能由一个 pthread_join() 函数获取,当有多个线程调用 pthread_join() 函数获取同一个线程的执行结果时,哪个线程最先执行 pthread_join() 函数,执行结果就由那个线程获得,其它线程的 pthread_join() 函数都将执行失败。

    创建线程demo

    #include 
    #include 
    #include 
    void *thr(void *args)
    {
        printf("Im a thread, pid = %d, tid = %u.\n", getpid(), pthread_self());
        sleep(5);
        return (void*)0;
    }
    
    int main()
    {
        pthread_t tid;
        pthread_create(&tid, nullptr, thr, nullptr);
        printf("Im main, pid = %d, tid = %u.\n", getpid(), pthread_self());
        
        int ret = 0;
        if ((ret = pthread_join(tid, nullptr)) > 0) { // 阻塞等待线程回收
            printf("join err : %d, %s\n", ret, strerror(ret));
        }
        
        printf("I will exit.\n");
        pthread_exit(nullptr);
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    线程退出注意事项:

    • 可以在线程中使用pthread_exit
    • 可以在线程中使用return(主控线程return代表退出进程)
    • exit代表退出整个进程
    • pthread_exit或者return返回的指针所指的内存单元必须是全局的或者是在堆空间分配的,不能是在栈空间分配的。
    • 主线程调用pthread_exit会阻塞等待其余线程都结束之后再退出。

    2.5 pthread_detach函数

    #include 
    int pthread_detach(pthread_t thread);
    
    • 1
    • 2

    功能:线程分离,线程主动与主线程断开关系,线程结束之后,其退出状态不由其他线程获取,而是自己直接自动释放,网络、多线程服务常用。pthread有两种状态joinable状态和unjoinable状态,一个线程默认的状态是joinable。如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当调用了pthread_join之后这些资源才会被释放。若是
    unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。unjoinable属性可以在pthread_create时指定,或在线程创建后在线程中pthread_detach自己, 如:pthread_detach(pthread_self()),将状态改为unjoinable状态,确保资源的释放。如果线程状态为 joinable,需要在之后适时调用pthread_join。
    参数:
    pthread_t thread:
    注:不能对一个已经分离的线程调用pthread_join,这样的调用将返回EINVAL错误。也就是说,一个线程调用了pthread_detach就不能再调用pthread_join。

    线程分离函数demo:

    #include 
    #include 
    #include 
    #include 
    void *thr(void *args)
    {
        printf("Im a thread, pid = %d, tid = %u.\n", getpid(), pthread_self());
        sleep(5);
        return (void*)0;
    }
    
    int main()
    {
        pthread_t tid;
        pthread_create(&tid, nullptr, thr, nullptr);
        printf("Im main, pid = %d, tid = %u.\n", getpid(), pthread_self());
        
        pthread_detach(tid); // 线程分离
        sleep(5);
        
        int ret = 0;
        if ((ret = pthread_join(tid, nullptr)) > 0) {
            printf("join err : %d, %s\n", ret, strerror(ret));
        }
    
        printf("I will exit.\n");
        pthread_exit(nullptr);
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    pthread_join函数阻塞等待回收失败

    Im main, pid = 525576, tid = 3930953536.
    Im a thread, pid = 525576, tid = 3922568960.
    join err : 22, Invalid argument
    I will exit.
    
    • 1
    • 2
    • 3
    • 4

    2.6 pthread_cancel函数

    #include 
    int pthread_cancel(pthread_t thread);
    
    • 1
    • 2

    功能:仅仅是向目标线程发送 Cancel 信号,至于目标线程是否处理该信号以及何时结束执行,由目标线程决定。对于接收 Cancel 信号后结束执行的目标线程,等同于该线程自己执行pthread_exit(PTHREAD_CANCELED)。也就是说,当一个线程被强制终止执行时,它会返回 PTHREAD_CANCELED 。因此,当对一个cancel的进程进行pthrean_join回收时,得到的返回值为-1
    参数:

    • pthread_t thread:用于指定发送 Cancel 信号的目标线程。

    杀死线程函数demo:

    #include 
    #include 
    #include 
    #include 
    void *thr(void *args)
    {
        while(1) {
            printf("Im a thread, pid = %d, tid = %u.\n", getpid(), pthread_self());
            sleep(1);
        }
        return (void*)0;
    }
    
    int main()
    {
        pthread_t tid;
        pthread_create(&tid, nullptr, thr, nullptr);
        printf("Im main, pid = %d, tid = %u.\n", getpid(), pthread_self());
        sleep(5);
        pthread_cancel(tid);
    
        void *ret;
        ret = pthread_join(tid, &ret);
        printf("thread exit with : %d\n", (int)ret);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2.7 进程属性控制函数

    #include 
    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    功能: 设置属性分离状态
    参数:

    • attr init 初始化的属性
    • detachstate
      PTHREAD_CREATE_DETACHED 线程分离
      PTHREAD_CREATE_JOINABLE 允许回收

    设置线程属性demo

    #include 
    #include 
    #include 
    #include 
    void *thr(void *arg)
    {
        printf("I am a thread.\n");
        return NULL;
    }
    
    int main(int argc, char *argv[])
    {
        pthread_attr_t attr;
        pthread_attr_init(&attr); // 初始化属性
        pthread_attr_getdetachstate(&arr, PTHREAD_CREATE_DETACHED); //设置属性分离
    
        pthread_t tid;
        pthread_create(&tid, &attr, thr, NULL);
        int ret = 0;
        if ((ret = pthread_join(tid, NULL)) > 0) {
            printf("join err: %d, %s\n", ret, strerror(ret));
        }
    
        pthread_attr_destroy(&attr);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    其他:

    • alias echomake = ‘cat ~/bin/makefile.template >> makefile’
    • set -o vi 设置shell里vi的快捷键,设置后vi的快捷键可以在shell中使用
    • 查看线程库版本:getconf GNU_LIBPTHREAD_VERSION
  • 相关阅读:
    TCP协议的相关特性
    如何借助cpolar内网穿透连接本地树莓派(1)
    Linux 生成复杂密码并且检查密码强度
    SQL优化
    集中采购如何节省企业时间和金钱?
    深度解析 集成运放 原理与运用
    2018ECCV Can 3D Pose be Learned from2D Projections Alone?
    ant design vue对话框关闭数据清空
    进程的概念,组成和特征(PCB)
    pytest + yaml 框架 - 3.全局仅登录一次,在用例中自动在请求头部添加Authentication token认证
  • 原文地址:https://blog.csdn.net/zhaopengvv/article/details/126694893