线程学习(二):线程终止、等待和分离

本文概念较多,而且有些绕,我尽量通俗讲。。。

线程终止

终止线程有三种方式终止

  1. 在线程函数内return
    线程内return相当于结束了线程运行的函数,退出了当前线程,但是注意主线程不能用此方法,主线程使用相当于main()函数执行结束,接着会调用exit(),终止了进程,进程内所有线程都会被强制退出
  2. 线程可以调用pthread_exit终止自己
    理论上说,其实在线程内return后,线程就会自动调用pthread_exit清理自己,但是实际情况中会由于编译器的差别有很多不同的地方,在主线程中调用pthread_exit仅会使主函数线程退出,而不会退出进程,由于其他线程没有退出,主线程会阻塞等待,直到其他线程全部退出再调用exit()退出

    以下这段话是我抄的,大概意思就是如果你在main函数中调用了pthread_exit,只会退出主线程,其他线程依旧是活跃的
    When you program with POSIX Threads API, there is one thing about pthread_exit() that you may ignore for mistake. In subroutines that complete normally, there is nothing special you have to do unless you want to pass a return code back using pthread_exit(). The completion won’t affect the other threads which were created by the main thread of this subroutine. However, in main(), when the code has been executed to the end, there could leave a choice for you. If you want to kill all the threads that main() created before, you can dispense with calling any functions. But if you want to keep the process and all the other threads except for the main thread alive after the exit of main(), then you can callpthread_exit() to realize it. And any files opened inside the main thread will remain open after its termination.

  3. 一个进程可以调用pthread_cancel来终止同一进程中的另一个线程
    假设我们用线程A调用pthread_cancel给线程B发送终止信号,如果成功则返回0,否则为非0值。但是发送成功并不意味着线程B会终止,实际使用中,需要在成功发送cancel信号后使用pthread_join(后面讲线程等待,这里先不用理解)等待进程B结束再继续执行其他操作,否则容易发生段错误(segmentation fault)

函数原型

pthread_exit

void pthread_exit(void* retval);
参数:
    retval保存了线程退出码,如果`int pthread_join(pthread_t thread, void **retval);`
的第二个参数不是NULL,那么将保存到retval中

pthread_cancel

int pthread_cancel(pthread_t thread);
参数:
    thread,线程ID
返回值:
    成功 0,错误 非0的error number

线程等待

为什么需要线程等待

线程和进程类似,退出了之后也有资源需要释放,不释放就会形成僵尸线程,造成内存泄漏,所以在线程退出后,我们需要回收资源,这就有了线程等待

函数原型

int pthread_join(pthread_t thread, void** retval)
参数:
    thread,线程ID
    retval,指向一个指针,这个指针指向的变量存储线程的返回值
返回值:
    成功->0 失败->错误码

调用pthread_join(thread)会让调用者陷入阻塞等待,直到被等待的thread进程退出,调用者才会继续执行,thread退出的方式不同,调用者收到的信号也是不同的,有以下几点:

  1. 如果thread线程是return退出的,那么retval指向的是thread线程的返回值
  2. 如果thread线程是调用pthread_exit退出的,那么retval指向的是pthread_exit传过来的参数
  3. 如果thread线程是被其他线程调用pthread_cancel异常终止的,那么retval指向的指针指向单元内部存储常量PTHREAD_CANCELED
  4. 如果thread的retval传的为NULL,那么说明我们不关心退出状态

线程分离

线程分离的场景

有的时候我们的线程需要结束,但是此时没有空闲线程去回收这个资源,我们不关心这个进程的退出状态但是资源必须回收,不回收会造成内存泄漏,所以有了线程分离,让该线程在运行结束时,自动回收资源,这样就不需要其他线程调用join阻塞去等待回收资源。
等待是为了获取函数的退出状态,回收资源,但是此时有了一个可以自动回收的方法,如果我们不关心函数的返回值,那么等待毫无意义,所以线程分离的好处就体现出来了。

detach和joinable

线程内部有一个属性,标志着线程是需要被回收还是自动回收,detach表示可以自动回收,joinable表示需要其他线程来回收,只有属性为joinable,这个线程才可以被其他线程回收,detach属性的线程是无法使用pthread_join来回收的。
默认情况下,新的线程创建出来都是joinable属性的,是需要其他线程回收的,如果我们不回收,那么后果就是内存泄漏。
由于detach属性和joinable属性是对立的,所以线程是detach时不能调用pthread_join,否则会报错。
当我们不关心线程的退出状态时,使用pthread_join是一种负担,我们可以设置线程属性为detach,告诉系统这个线程结束之后自动回收资源。

函数原型

int pthread(pthread_t thread);
参数:
    thread,线程ID
返回值:
    成功->0 失败->错误码

线程分离可以是其他线程帮忙分离,也可以是自己分离,一般我们都习惯在一个线程中调用pthread_detach(pthread_self());来让线程自行回收资源

代码

github地址

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

void* listen_music(void* t)
{
    //此函数内使用线程退出,主线程使用线程等待
    int time = (int)t;
    int i = 0;
    for (; i < 100; i++)
    {
        printf("Listen to music~~\n");
        sleep(1);
        if (i == time)
        {
            pthread_exit(0);
        }
    }
    return NULL;
}

void* do_homework(void* t)
{
    //写作业线程直接使用线程分离,不再需要等待
    int time = (int)t;
    while (time--)
    {
        printf("Do homework~~\n");
        sleep(1);
    }
    pthread_detach(pthread_self());
    return NULL;
}

int main()
{
    pthread_t music, homework;
    int ret;
    int time = 10;
    ret = pthread_create(&music, NULL, listen_music, (void*)time);
    if (ret < 0)
    {
        perror("pthread_create failed");
        return -1;
    }
    ret = pthread_create(&homework, NULL, do_homework, (void*)time);
    if (ret < 0)
    {
        perror("pthread_create failed");
        return -1;
    }
    //线程等待
    pthread_join(music);
    return 0;
}

执行结果
运行结果

发布了89 篇原创文章 · 获赞 96 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/Boring_Wednesday/article/details/82718797