简 述: 上一篇讲解了多线程的入门的知识,如何创建多线程程序,以及从虚拟地址空间和 PCB 分析线程的一些细节。本章再讲述一些线程操作相关的函数:

  • pthread_exit(): 退出一个线程,不会影响其他线程的
  • pthread_jion(): 阻塞等待线程退出,获取线程退出的状态
  • pthread_detach(): 设置线程分离的属性
  • pthread_cancel(): 杀死(取消)一个线程
  • pthread_equal(): 判断两个线程是否相等,预留函数

[TOC]


本文初发于 “偕臧的小站“,同步转载于此。


编程环境:

  💻: uos20 📎 gcc/g++ 8.3 📎 gdb8.0

  💻: MacOS 10.14 📎 gcc/g++ 9.2 📎 gdb8.3


单个线程退出 pthread_exit():

  • 作用: 一个进程的退出使用 exit(0); 而退出一个线程,使用 pthread_exit(NULL)
void pthread_exit(void *value_ptr);
  • 参数:

    • value_ptr:必须指向全局,或者堆。是一个传出参数,用来在该线程结束推出的时候,传输一个内容。若是指向局部变量,该线程被销毁了的话,其他线程可能访问不到此块数据块。
  • 写一个例子:

    #include <stdio.h>
    #include <unistd.h>
    #include <pthread.h>
    
    int num = 13; //设置为全局变量,在全局区域,共享
    
    void* myfun(void* arg);
    
    int main(int argc, char *argv[])
    {
        void* p = (void *)&num;  //传一个地址进去(voi* 也是 4 个字节)
        pthread_t id[5] = {0};
        for (int i = 0; i < 5; i++) {
            pthread_create(&(id[i]), NULL, myfun, p);
            printf("i = %d, thread id: %ld\n", i, id[i]);
        }
    
        // pthread_exit(nullptr);  //终止主线程
        int j = 0;
        while (true) {
            sleep(1);
            printf ("main thread-----%d------\n", j++);
        }
        
        return 0;
    }
    
    void* myfun(void* arg)
    {
        for (int i = 0; i < 5; i++) {
            printf ("child thread-----%d------\n", i);
    
            if (i == 2) {
                 _exit(0); //退出进程
                // pthread_exit(nullptr);  //终止主线程
            }        
        }
    
        return nullptr;
    }
  • 运行结果:


阻塞等待退出线程退出 pthread_jion():

  • 作用:

    阻塞等待线程退出,获取线程退出的状态

int pthread_join(pthread_t thread, void **value_ptr);
  • 参数:

    • thread: 要回收的子线程的 id
    • value_ptr:读取线程退出时候的携带信息状态;指向的内存和 pthread_exit 的参数是指向同一块内存地址 。在上一个函数 pthread_exit(void *value_ptr) 中,线程退出的时候,传出来了全局变量的指针; 而这里第二个参数,此二级指针就可以读出来这个值。
  • 写了一例子:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    
    int number = 1234; //要是一个全局的变量哦,或者是堆的空间
    void* myfun(void* arg);
    
    int main(int argc, char *argv[])
    {
        pthread_t pthread = 0;
        int ret = pthread_create(&pthread, NULL, myfun, NULL);
    
        if (ret != 0)
            printf("error: %s\n", strerror(ret));
    
        printf("parent thread id: %ld\n", pthread_self);
    
        void* ptr = nullptr;
    
        pthread_join(pthread, &ptr); //阻塞等待子线程退出,并且回收 pcb
        printf("number = %d\n", *((int*)ptr));
    
        int i = 0;
        while (i < 10 ) {
            printf("parent i = %d\n", i++);
        }
        
        return 0;
    }
    
    void* myfun(void* arg)
    {
        printf("child thread id: %ld\n", pthread_self());
    
        for (int i = 0; i < 5; i++) {
            if (i == 2) {
                // int number = 1234;   //若是为栈里面的数据,则会组线程里面,会崩溃或者失败
                pthread_exit(&number);  //此处的地址,必须是指向堆或者全局变量
            }
    
            printf("child i = %d\n", i);
        }
    
        return NULL;
    }
  • 运行效果:


线程分离 pthread_detach():

在用 pthread_create 创建子线程的时候,第三个参数,是可以设置为此属性的;正常情况下,是由子线程死亡,是由父线程来释放遗留的资源;但如果设置了此线程分离的属性,那么子线程是在创建的时候,就独立于父线程,其死亡时候资源也是由自己来释放。

int pthread_detach(pthread_t thread);
  • 调用该函数之后,就不在需要调用 pthread_jion() 了

杀死(取消)线程 pthread_cancel():

int pthread_cancel(pthread_t thread);
  • 注意:
    • 要杀死子线程对应的处理的函数的内部,必须做一次系统调用。
    • 系统调用函数有,eg: open,write, printf 等,这次都是最终会调用到系统层及的函数。
    • 不涉及到系统层级调用,eg: int a = 10;

判断线程是否相等 pthread_equal():

是一个预留函数 ,通过线程号 判断两个线程是否相等;目前不需要使用该函数。若是以后线程变为返回一个结构体。就可以用此预留函数(修改一下实现)来判断两个线程是否实现了。

int pthread_equal(pthread_t t1, pthread_t t2);

下载地址:

17_thread

欢迎 star 和 fork 这个系列的 linux 学习,附学习由浅入深的目录。