【Linux多线程】线程的终止、等待和分离

文章目录

线程终止

下面给出终止线程的三种方式:

  1. 正常退出:
    • 线程执行完它的函数之后return自动结束
    • 线程显示调用pthread_exit函数退出
  2. 强制终止:
    • 一个线程可以被另一个线程通过pthread_cancel函数强制退出
  3. 进程终止:
    • 一个进程终止,那么该进程的所有线程都会终止

下面将分别模拟这三种终止线程的方式:

正常退出

return 退出

给出下面代码模拟线程return终止

cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;

void *rout(void *arg)
{
    int cnt = 3;
    while (cnt--)
    {
        cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;
        sleep(1);
    }
    cout << "phread end....." << endl;
    sleep(1);
    return NULL;
}

int main()
{
    pthread_t tid;
    int num = 10;
    int res = pthread_create(&tid, NULL, rout, (void *)(&num));
    pthread_join(tid, NULL); // 等待线程结束
    sleep(20);
    cout << "wait success! main thread end!" << endl;
    return 0;
}

观察上面代码运行情况,发现当线程return之后确实被终止了(上图多出来一个线程是库里面的管理线程,不必理会)。

pthread_exit函数终止线程

pthread-exit函数用于终止当前线程

函数原型:

cpp 复制代码
void pthread_exit(void *value_ptr);
  • value_ptr是一个任意类型的指针,表示线程的退出状态。其它线程可以通过pthread_join函数获取这个状态

给出下面代码观察线程终止

cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;

void *rout(void *arg)
{
    int cnt = 3;
    int *status = new int(20);
    while (true)
    {
        cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;
        sleep(1);
        if (cnt == 0)
        {
            cout << "phread exit....." << endl;
            pthread_exit((void *)(status));
        }
        cnt--;
    }
    // cout << "phread end....." << endl;
    // sleep(1);
    return NULL;
}

int main()
{
    pthread_t tid;
    int num = 10;
    int res = pthread_create(&tid, NULL, rout, (void *)(&num));
    void *status = NULL;
    pthread_join(tid, &status); // 等待线程结束
    sleep(5);
    cout << "wait success! main thread end! status: " << *(int *)(status) << endl;
    return 0;
}

需要注意,pthread_exit或者return返回的指针指向的内存单元应该是全局的,因为线程终止之后其函数栈帧会销毁,之后才会返回一个void*指针。

pthread_cancel强制终止线程

功能:取消一个执行中的线程

函数原型:

cpp 复制代码
int pthread_cancel(pthread_t thread);
  • thread表示要删除的线程id(用户级)
  • 成功返回0,否则返回错误码

给出代码样例,观察pthread_cancel函数的使用

cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;

void *rout(void *arg)
{
    int cnt = 3;
    int *status = new int(20);
    while (true)
    {
        cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;
        sleep(1);
        // if (cnt == 0)
        // {
        //     cout << "phread exit....." << endl;
        //     pthread_exit((void *)(status));
        // }
        // cnt--;
    }
    // cout << "phread end....." << endl;
    // sleep(1);
    return NULL;
}

int main()
{
    pthread_t tid;
    int num = 10;
    int res = pthread_create(&tid, NULL, rout, (void *)(&num));
    void *status = NULL;

    sleep(5);
    pthread_cancel(tid); // 强制终止tid
    cout << "thread is end" << endl;
    pthread_join(tid, &status); // 等待线程结束
    sleep(3);
    cout << "wait success! main thread end! status: " << *(int *)(status) << endl;
    return 0;
}

在主线程中调用pthread_cancel函数强制终止了执行rout函数的线程。

进程终止

进程终止,该进程的所有线程都终止。比如在函数中调用exit终止进程。

cpp 复制代码
void *rout(void *arg)
{
    int cnt = 3;
    int *status = new int(20);
    while (true)
    {
        cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;
        sleep(1);
        if (cnt == 0)
        {
            cout << "phread exit....." << endl;
            exit(0);
        }
        cnt--;
    }
    return NULL;
}

一旦调用exit或者异常而终止进程,该进程的所有线程都玩完。道理很简单,这里就不做演示了。

线程等待

为什么需要等待线程?

其实已经退出的线程并没有完全"结束",其栈帧并没有随着线程终止马上就释放,仍然在进程的地址空间里。并且,如果不对这些已经终止但是还没有被释放空间的线程做处理,往后继续创建新线程都不会复用前面退出线程的地址空间,这就造成了资源的浪费。这种情况其实非常像我们之前谈过的僵尸进程问题。

等待线程终止就是提醒内核可以释放这个线程的资源了 。等待线程终止其实也是为了确保线程完成任务。有时主线程或者其他线程需要等待某一个线程任务完成之后才能继续执行。等待一个线程结束,其实就是让终止的线程退出时"通知 "一下其它线程,可以不关心退出线程的返回结果。此外,等待线程终止在某些情况下能保证数据的完整性,比如线程一处理上半段数据,线程二处理下半段数据,如果其中任何一个线程没有终止,那总数据就会不完整。

为了实现线程终止时的等待问题,linux提供了pthread_join函数。

pthread_join函数

作用:阻塞调用该函数线程,直到目标线程终止。

函数原型:

cpp 复制代码
#include<pthread.h>
int pthread_join(pthread_t thread,void** retval);

其中:

  • thread表示所等待线程的线程标识符
  • retval是一个输出型参数,指向等待线程的退出信息
  • 成功返回0,否则返回错误码

值得注意的是,线程终止方式的不同,其通过pthread_join得到的退出信息也就不同。比如:

  1. 目标线程通过return 终止(或者是pthread_exit),retval所指向的单元里存放的就是目标线程执行函数的返回值。
    观察下面代码,分析线程return终止时,pthread_join得到的返回值
cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;

void *rout(void *arg)
{
    int cnt = 3;
    int *status = new int(20);
    int num = 20;
    while (cnt--)
    {
        cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;
        sleep(1);
    }
    // pthread_exit((void *)status);
    return (void *)status;
}

int main()
{
    pthread_t tid;
    int num = 10;
    int res = pthread_create(&tid, NULL, rout, (void *)(&num));
    void *status = NULL;
    cout << "main pthread wait...." << endl;
    pthread_join(tid, &status); // 等待线程结束
    sleep(3);
    cout << "wait success! main thread end! status: " << *(int *)(status) << endl;
    return 0;
}
  1. 如果线程是被别的线程通过调用pthread_cancel函数强制终止掉,retval所指向单元存放的就是常数PTHREAD_ CANCELED

给出代码样例观察结果:

cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;

void *rout(void *arg)
{
    int cnt = 3;
    int *status = new int(20);
    int num = 20;
    while (cnt--)
    {
        cout << "thread id :  " << pthread_self() << "  i am thread num:  " << *(int *)arg << endl;
        sleep(1);
    }
    pthread_exit((void *)status);
    // return (void *)status;
}

int main()
{
    pthread_t tid;
    int num = 10;
    int res = pthread_create(&tid, NULL, rout, (void *)(&num));
    void *status = NULL;
    sleep(2);
    pthread_cancel(tid);
    pthread_join(tid, &status); // 等待线程结束
    if (status == PTHREAD_CANCELED)
    {
        cout << "pthread is cancel" << endl;
    }
    return 0;
}

retval所指向单元存放的就是常数PTHREAD_ CANCELED得证。

  1. 如果不关心某一个线程的退出信息,可以return NULL。比如:
cpp 复制代码
void* thread_function(void* arg) {
    // 线程的工作
    printf("Thread is running\n");
    return NULL;
}

总结:

分离线程

分离线程实际上是线程的一种状态,这种状态表示该进程不需要被等待,且线程退出后会自动释放资源。对于主线程来说,有些线程独立执行任务,其它线程没有必要再调用pthread_join阻塞等待。这样提升了整体的效率。一般来说,创建的新线程默认都是joinable的,也就是需要被等待的,我们可以通过pthread_detach函数来改变这一性质。

pthread_detach函数

功能:分离一个目标线程,使该线程终止后自动释放资源,不需要被等待

函数原型:

cpp 复制代码
#include<pthread.h>
int pthread_detach(pthread_t thread);
  • thread表示分离的目标线程,也可以是调用该函数的线程本身
  • 成功放回0,否则-1

给出代码样例,观察pthread_detach函数的使用

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
using namespace std;

void *thread_run(void *arg)
{
    pthread_detach(pthread_self());
    printf("%s\n", (char *)arg);
    return NULL;
}

int main(void)
{
    pthread_t tid;
    if (pthread_create(&tid, NULL, thread_run, (void *)"thread1 run...") != 0)
    {
        printf("create thread error\n");
        return 1;
    }

    int ret = 0;
    sleep(1); // 很重要,要让线程先分离,再等待

    if (pthread_join(tid, NULL) == 0)
    {
        printf("pthread wait success\n");
        ret = 0;
    }
    else
    {
        printf("pthread wait failed\n");
        ret = 1;
    }
    return ret;
}

分析上述代码,因为执行thread_run函数的线程设置成了分离状态,函数结束之后自动释放资源。此时再去用pthread_join函数去等待这个线程就会得到返回值0。

相关推荐
Peter_chq21 分钟前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml444 分钟前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~1 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616881 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
一坨阿亮1 小时前
Linux 使用中的问题
linux·运维
aloha_7891 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
记录成长java2 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
睡觉谁叫~~~2 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
dsywws2 小时前
Linux学习笔记之vim入门
linux·笔记·学习
程序媛小果2 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot