首页 > 系统相关 >【Linux多线程】线程的终止、等待和分离

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

时间:2024-06-05 23:28:58浏览次数:26  
标签:include int void Linux 线程 pthread 终止 多线程

文章目录

线程终止

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

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

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

正常退出

return 退出

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

#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函数用于终止当前线程
函数原型:

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

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

#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强制终止线程

功能:取消一个执行中的线程
函数原型:

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

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

#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终止进程。

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函数

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

#include<pthread.h>
int pthread_join(pthread_t thread,void** retval);

在这里插入图片描述
其中:

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

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

  1. 目标线程通过return 终止(或者是pthread_exit),retval所指向的单元里存放的就是目标线程执行函数的返回值。
    观察下面代码,分析线程return终止时,pthread_join得到的返回值
#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

给出代码样例观察结果:

#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。比如:
void* thread_function(void* arg) {
    // 线程的工作
    printf("Thread is running\n");
    return NULL;
}

总结:

分离线程

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

pthread_detach函数

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

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

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

#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。

标签:include,int,void,Linux,线程,pthread,终止,多线程
From: https://blog.csdn.net/qq_62987647/article/details/139474911

相关文章

  • 新手上路:Linux虚拟机创建与Hadoop集群配置指南①(未完)
    一、基础阶段Linux操作系统:创建虚拟机1.创建虚拟机打开VM,点击文件,新建虚拟机,点击自定义,下一步下一步这里可以选择安装程序光盘映像文件,我选择稍后安装选择linux系统位置不选C盘,创建一个新的文件夹VM来放置虚拟机,将虚拟机名字改为master方便后续识别(也可以改为其他......
  • 内核线程被调度执行的时候需要一个地址空间,这个地址空间是从哪里来的
    内核线程被调度执行时确实需要一个地址空间,但这个地址空间并不是为每个内核线程独立创建的。内核线程运行在操作系统的内核空间中,而不是在用户空间。以下是内核线程执行时地址空间的来源和管理方式:地址空间来源共享内核地址空间:所有内核线程共享内核地址空间,这包括内核代码......
  • 多线程interrupt()方法
    interrupt()方法:配合isInterrupted()方法可以合理打断线程,让线程处理好事务后停止。打算一个非阻塞状态的线程效果publicclasstest{publicstaticvoidmain(String[]args){Runnabler=()->{while(true){booleaninterrup......
  • 内核线程为什么没有地址空间
    内核线程没有独立的地址空间,这是因为内核线程是在操作系统内核空间中运行的,内核空间本身是所有进程共享的。以下是一些更详细的解释:内核与用户态的区别:操作系统通常将内存分为用户空间和内核空间。用户空间是为用户进程提供的,它们有各自的虚拟地址空间,相互之间隔离,不能直接访问......
  • 【JUC】4-FutrueTask结合线程池的应用
    1、通过线程池提交FutrueTask异步任务1publicstaticvoidmain(String[]args)throwsExecutionException,InterruptedException,TimeoutException{23longstart=System.currentTimeMillis();4ExecutorServiceexecutorService=Executors.n......
  • 【JUC】1-Java线程的启动
    以Thread创建线程为例:1Threadthread=newThread(){2@Override3publicvoidrun(){4log.info("createandstartathreadthroughcreatingaThreadObject");5}6};7thread.start();......
  • linux信号集与信号掩码-保护信号处理程序,确保进程正确运行
    在Linux环境下,当进程收到信号时,如何优雅地处理并确保程序的正常运行?这就需要借助信号集和信号掩码的功能。本文将为你揭开信号集和信号掩码的神秘面纱,并通过生动的代码示例,让你彻底掌握在C++程序中使用它们的技巧。一、信号集:表示信号的数据结构信号集(signalset)是......
  • Java:实现使用CountDownLatch实现线程同步(附完整源码)
    Java:实现使用CountDownLatch实现线程同步我可以帮你实现使用CountDownLatch实现线程同步的Java代码。以下是一个简单的示例:​importjava.util.concurrent.CountDownLatch;​publicclassThreadSyncExample{publicstaticvoidmain(String[]args)t......
  • 【Linux】(六)—— vim编辑器
    vim文件编辑器Vim(ViImproved)是一个高度可配置的文本编辑器,最初基于UNIX下的Vi编辑器发展而来,广泛用于程序开发和系统管理中。vim编辑器可以只通过终端命令即可编写修改文件,不需要和gedit一样需要打开类似于记事本的窗口。Vim以其高效、灵活和强大著称,但对初学者来说可能有......
  • Linux基础 (十四):socket网络编程
         我们用户是处在应用层的,根据不同的场景和业务需求,传输层就要为我们应用层提供不同的传输协议,常见的就是TCP协议和UDP协议,二者各自有不同的特点,网络中的数据的传输其实就是两个进程间的通信,两个进程在通信时,传输层使用TCP协议将一方进程的应用层的数据传输给另一......