Linux —— 线程控制(1)

目录

[2. Linux 进程 vs 线程 -- 哪些资源共享,哪些独占](#2. Linux 进程 vs 线程 -- 哪些资源共享,哪些独占)

[2.1 共识](#2.1 共识)

[2.2 进程和线程](#2.2 进程和线程)

[2.3 进程的多个线程共享](#2.3 进程的多个线程共享)

[2.4 如何看待之前的单进程?](#2.4 如何看待之前的单进程?)

[3. Linux线程控制](#3. Linux线程控制)

[3.1 直接使用各个线程控制的接口](#3.1 直接使用各个线程控制的接口)

[3.2 创建线程](#3.2 创建线程)

[3.3 验证历史讲理论的各个细节](#3.3 验证历史讲理论的各个细节)

[3.3.1 全局变量共享:](#3.3.1 全局变量共享:)

[3.3.2 多线程其它函数共享:](#3.3.2 多线程其它函数共享:)

[3.3.3 堆区也是共享的:](#3.3.3 堆区也是共享的:)

[3.3.4 main函数执行完直接先退出的话,会发生什么??](#3.3.4 main函数执行完直接先退出的话,会发生什么??)

[3.4 pthread_join,是在线程库中等待线程的一个函数。](#3.4 pthread_join,是在线程库中等待线程的一个函数。)

[3.4.1 创建多个线程,等待多个线程:](#3.4.1 创建多个线程,等待多个线程:)

[3.5 验证线程崩溃问题:](#3.5 验证线程崩溃问题:)

[3.6 线程的退出方法:(三个方法)](#3.6 线程的退出方法:(三个方法))

[方法1. return nullptr;](#方法1. return nullptr;)

[exit(10); ( 线程的退出不能使用exit() )](#exit(10); ( 线程的退出不能使用exit() ))

[方法2. pthread_exit()](#方法2. pthread_exit())

[pthread_self(); 自己获取自己的线程id](#pthread_self(); 自己获取自己的线程id)

方法3:pthread_cancel(要取消的线程id):可以让一个线程去取消另一个线程

[4. 线程的传参和返回值](#4. 线程的传参和返回值)

[4.1 返回值](#4.1 返回值)

[4.1.1 从编码层面上理解返回值](#4.1.1 从编码层面上理解返回值)

[4.1.2 pthread_join() 第二个参数的问题](#4.1.2 pthread_join() 第二个参数的问题)


2. Linux 进程 vs 线程 -- 哪些资源共享,哪些独占

2.1 共识

  • 进程间具有独立性。进程更强调独占,偶尔要进行资源共享
  • 线程共享地址空间,也就共享进程资源。线程更强调资源共享,偶尔要进行资源独占

2.2 进程和线程

  • 进程是资源分配的基本单位
  • 线程是调度的基本单位
  • 线程共享进程数据,但也拥有自己的一部分 "私有" 数据:(后面会提到,其实线程并没有私有的数据,只不过是将这部分资源分配给线程了,别人想拿的话其实也是可以拿的)
  • 线程ID (重点)
  • 一组寄存器,线程的上下文数据 (重点) ---> 说明线程是要被调度的
  • 栈 (重点) ---> 说明线程除了要被调度、切换,线程本身在被运行时会产生临时数据,线程自己也有函数调用,栈帧结构
  • errno
  • 信号屏蔽字
  • 调度优先级

2.3 进程的多个线程共享

同一个进程内的多个线程,大部分是资源全都是共享的。所有的轻量级进程 / 线程 的概念都属于同⼀地址空间,因此 Text Segment 、 Data Segment 都是共享的,如果定义⼀个函数,在各线程中

都可以调⽤,如果定义⼀个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

  • 文件描述符表 (一个进程打开文件会有文件描述符表,每一个进程都会指向主线程的同样的一张文件描述符表,新的task_struct的大部分属性同样来自于主线程,从主线程拷贝到新线程中的)
  • 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
  • 当前的工作目录
  • 用户id 和 组id

线程和进程的关系:

2.4 如何看待之前的单进程?

之前的单进程,是具有一个线程执行流的进程

3. Linux线程控制

3.1 直接使用各个线程控制的接口

  • 与线程相关的函数是库函数,都遵守POSIX的标准,命名上都是以"pthread_" 打头
  • 使用这些函数,都要包含头文件<pthread.h>
  • 链接这些线程函数库时要使用编译器命令的 "-lpthread" 的选项

3.2 创建线程

创建线程使用的函数:pthread_create

bash 复制代码
#Makefile
test_thread : testThread.cc
	g++ -o $@ $^ -lpthread -std=c++11
.PHONY:clean
clean:
	rm -f test_thread
cpp 复制代码
//testThread.cc
#include <iostream>
#include <string>
#include <unistd.h>
#include <pthread.h>

void *thread_routine(void* args)
{
    std::string name = static_cast<const char*>(args);
    //新线程
    while(true)
    {
        printf("new thread...\n");
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void *)"thread-1");
    (void)n;  //避免警告

    //主线程
    while(true)
    {
        printf("main thread...\n");
        sleep(1);
    }
}

运行结果:

我们编程时认为的两个线程,其实在内核中对应的就是两个轻量级进程。

pthread库头文件就在以下目录下:

所以不用告诉我在哪里找,所对应的库文件:

打印一下 tid :

cpp 复制代码
int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void *)"thread-1");
    (void)n;  //避免警告

    printf("main create a new thread, new thread id is: %lu\n", tid);

    //主线程
    while(true)
    {
        printf("main thread...\n");
        sleep(1);
    }
}

运行结果:

打印出来的值很大!!以16进制输出

3.3 验证历史讲理论的各个细节

3.3.1 全局变量共享:

cpp 复制代码
#include <iostream>
#include <string>
#include <unistd.h>
#include <pthread.h>

volatile int gval = 100;

void *thread_routine(void* args)
{
    std::string name = static_cast<const char*>(args);
    //新线程
    while(true)
    {
        printf("new thread..., gval: %d, &gval: %p\n", gval, &gval);
        gval++;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void *)"thread-1");
    (void)n;  //避免警告

    printf("main create a new thread, new thread id is: 0x%lx\n", tid);

    //主线程
    while(true)
    {
        printf("main thread..., gval: %d, &gval: %p\n", gval, &gval);
        sleep(1);
    }
}

定义一个全局变量,在新线程中对全局变量做++,最后的结果是全局变量共享:

3.3.2 多线程其它函数共享:

cpp 复制代码
#include <iostream>
#include <string>
#include <unistd.h>
#include <pthread.h>

volatile int gval = 100;

std::string fun()
{
    return "我是另一个函数";
}

void *thread_routine(void* args)
{
    std::string name = static_cast<const char*>(args);
    //新线程
    while(true)
    {
        printf("new thread..., gval: %d, &gval: %p , %s\n", gval, &gval, fun().c_str());
        gval++;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void *)"thread-1");
    (void)n;  //避免警告

    printf("main create a new thread, new thread id is: 0x%lx\n", tid);

    //主线程
    while(true)
    {
        printf("main thread..., gval: %d, &gval: %p, %s\n", gval, &gval, fun().c_str());
        sleep(1);
    }
}

运行结果:

多线程其他函数共享 ---> 所以函数重入的概念 就有了!!!

新线程在调用该函数,主线程也在调用该函数,此时两个执行流同时进入同一个函数,这个现象就叫做函数被重入了!!如果在某个时刻的重入导致了系统出错,那么这个函数就是不可重入函数;如果无论在什么情况下重入都不会出错,那么它是可重入函数。

在多进程中,函数不存在重入的问题。

3.3.3 堆区也是共享的:

补充:一旦线程被创建了,主线程和新线程在OS内部都是两个task_struct,task_struct 会以相同优先级的方式链入到大O(1)的算法中,放在同一张哈希表中的链表中当中,所以一般是不会规定主线程和新线程的顺序,但是今天在之后的代码中我们想让新线程将空间创建出来,所以让主线程中执行 sleep(2); 语句。

cpp 复制代码
#include <iostream>
#include <string>
#include <unistd.h>
#include <pthread.h>

volatile int gval = 100;

int *data = nullptr;  //定义一个指针

std::string fun()
{
    return "我是另一个函数";
}

void *thread_routine(void* args)
{
    std::string name = static_cast<const char*>(args);
    data = new int(10);
    //新线程
    while(true)
    {
        printf("new thread..., gval: %d, &gval: %p , %s, data: %p, %d\n", 
                gval, &gval, fun().c_str(), data, *data);
        gval++;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void *)"thread-1");
    (void)n;  //避免警告

    printf("main create a new thread, new thread id is: 0x%lx\n", tid);

    //主线程
    sleep(2);
    while(true)
    {
        printf("main thread..., gval: %d, &gval: %p, %s,data: %p, %d\n", 
                gval, &gval, fun().c_str(), data, *data);
        sleep(1);
    }
}

运行结果:

原则上,堆空间也是共享的。为什么说的是原则上呢??新线程申请堆空间,是不会让全局指针指向堆空间的,是会在新线程内部定义自己的指针。虽然这个堆空间是新线程内部自己申请的,但是如果你想要访问的话,是有办法来访问到的!!!

cpp 复制代码
#include <iostream>
#include <string>
#include <unistd.h>
#include <pthread.h>

volatile int gval = 100;

int *data = nullptr;  //定义一个指针

std::string fun()
{
    return "我是另一个函数";
}

void *thread_routine(void* args)
{
    std::string name = static_cast<const char*>(args);
    // 堆空间,就是该线程申请的,往往只需要让对应的线程知道
    // 这部分堆空间起始虚拟地址,就可以叫做这个堆是该线程拥有的
    int *data = new int(10);  
    //新线程
    while(true)
    {
        printf("new thread..., gval: %d, &gval: %p , %s, data: %p, %d\n", 
                gval, &gval, fun().c_str(), data, *data);
        gval++;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void *)"thread-1");
    (void)n;  //避免警告

    printf("main create a new thread, new thread id is: 0x%lx\n", tid);

    //主线程
    sleep(2);
    while(true)
    {
        printf("main thread..., gval: %d, &gval: %p, %s,data: %p, %d\n", 
                gval, &gval, fun().c_str(), data, *data);
        sleep(1);
    }
}

其实,这个堆空间往往是全局的,只不过在申请的时候只有该线程知道罢了,其它线程不知道,对于该线程来说这个堆空间就是属于该线程的。

3.3.4 main函数执行完直接先退出的话,会发生什么??

cpp 复制代码
#include <iostream>
#include <string>
#include <unistd.h>
#include <pthread.h>

volatile int gval = 100;

int *data = nullptr; // 定义一个指针

std::string fun()
{
    return "我是另一个函数";
}

// 新线程
void *thread_routine(void *args)
{
    std::string name = static_cast<const char *>(args);
    // 堆空间,就是该线程申请的,往往只需要让对应的线程知道
    // 这部分堆空间起始虚拟地址,就可以叫做这个堆是该线程拥有的
    int *data = new int(10);
    int cnt = 10;
    while (cnt--)
    {
        printf("new thread..., gval: %d, &gval: %p , %s, data: %p, %d\n",
               gval, &gval, fun().c_str(), data, *data);
        gval++;
        sleep(1);
    }
    return nullptr;  //如果线程执行完了自己的入口函数,表明该线程结束退出
}

int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void *)"thread-1");
    (void)n; // 避免警告

    printf("main create a new thread, new thread id is: 0x%lx\n", tid);
    sleep(3);


    return 0;  //如果main函数结束,表示主线程结束,同时也表示,当前进程结束
}

运行结果:

主线程退出,新线程自己也主动退出了。

主线程退出,表示进程结束,进程结束 ---- 释放资源!!

结论是:进程结束,所有的线程全部退出,哪怕没有执行完!!!

所以,在多线程代码中,主/新线程谁先运行不关注,但是,往往我们想让主线程最后退出!!!

所以:线程也要进行等待,类似进程的wait!

要对新线程进行等待:

1. 主线程可能先退出,进而导致整个进程全部都挂掉

**2. 也会造成类似僵尸进程的问题!!**之前进程的退出时,所有的资源都可以被释放了,唯独要保留PCB,线程也是一样的,但是在linux系统中不存在线程,只存在轻量级进程。一旦线程退出,轻量级进程是看不到的。所以查不到僵尸进程这样的概念,也查不到僵尸轻量级进程这样的概念。

3.4 pthread_join,是在线程库中等待线程的一个函数。

cpp 复制代码
#include <iostream>
#include <string>
#include <unistd.h>
#include <pthread.h>

volatile int gval = 100;

int *data = nullptr; // 定义一个指针

std::string fun()
{
    return "我是另一个函数";
}

// 新线程
void *thread_routine(void *args)
{
    std::string name = static_cast<const char *>(args);
    // 堆空间,就是该线程申请的,往往只需要让对应的线程知道
    // 这部分堆空间起始虚拟地址,就可以叫做这个堆是该线程拥有的
    int *data = new int(10);
    int cnt = 5;
    while (cnt--)
    {
        printf("new thread..., gval: %d, &gval: %p , %s, data: %p, %d\n",
               gval, &gval, fun().c_str(), data, *data);
        gval++;
        sleep(1);
    }
    return nullptr;  //如果线程执行完了自己的入口函数,表明该线程结束退出
}

int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void *)"thread-1");
    (void)n; // 避免警告

    printf("main create a new thread, new thread id is: 0x%lx\n", tid);

    pthread_join(tid, nullptr);
    printf("main end...\n");

    return 0;  // 如果main函数结束,表示主线程结束,同时也表示,当前进程结束  --- 释放资源
               // 结论是:进程结束,所有线程全部退出,哪怕没有执行完
}

运行结果:

3.4.1 创建多个线程,等待多个线程:

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>

volatile int gval = 100;

int *data = nullptr; // 定义一个指针

std::string fun()
{
    return "我是另一个函数";
}

// 新线程
void *thread_routine(void *args)
{
    std::string name = static_cast<const char *>(args);
    // 堆空间,就是该线程申请的,往往只需要让对应的线程知道
    // 这部分堆空间起始虚拟地址,就可以叫做这个堆是该线程拥有的
    int *data = new int(10);
    int cnt = 5;
    while (cnt--)
    {
        printf("new thread..., gval: %d, &gval: %p , %s, data: %p, %d\n",
               gval, &gval, fun().c_str(), data, *data);
        gval++;
        sleep(1);
    }
    return nullptr; // 如果线程执行完了自己的入口函数,表明该线程结束退出
}

int gnum = 10;

int main()
{

    std::vector<pthread_t> tids;
    for (int i = 0; i < gnum; i++)
    {
        pthread_t tid;
        int n = pthread_create(&tid, nullptr, thread_routine, (void *)"thread-1");
        (void)n; // 避免警告
        tids.push_back(tid);
    }

    for (auto &tid : tids)
        printf("main create a new thread, new thread id is: 0x%lx\n", tid);
   
    for (auto &tid : tids)
    {
        pthread_join(tid, nullptr);
        printf("new thread end...,%lu\n", tid);
    }

    printf("main end...\n");

    return 0; // 如果main函数结束,表示主线程结束,同时也表示,当前进程结束  --- 释放资源
              // 结论是:进程结束,所有线程全部退出,哪怕没有执行完
}

运行结果:

接下来,要给线程传参:

运行结果:

多运行几次,每次运行出来的结果都不同:

为什么会出现这样的问题呢??

正确书写:

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>

volatile int gval = 100;

int *data = nullptr; // 定义一个指针

std::string fun()
{
    return "我是另一个函数";
}

// 新线程
void *thread_routine(void *args)
{
    std::string name = static_cast<const char *>(args);
    // 堆空间,就是该线程申请的,往往只需要让对应的线程知道
    // 这部分堆空间起始虚拟地址,就可以叫做这个堆是该线程拥有的
    // int *data = new int(10);
    int cnt = 5;
    while (cnt--)
    {
        printf("new thread is running, name is : %s\n",name.c_str());
        sleep(1);
    }
    return nullptr; // 如果线程执行完了自己的入口函数,表明该线程结束退出
}

int gnum = 5;

int main()
{

    std::vector<pthread_t> tids;
    for (int i = 0; i < gnum; i++)
    {
        pthread_t tid;
        //构建新线程的名称
        // char idbuffer[64];
        char *p = new char[64];  //申请一段堆空间
        snprintf(p, 64, "thread-%d",i+1);

        int n = pthread_create(&tid, nullptr, thread_routine, p);
        (void)n; // 避免警告
        tids.push_back(tid);
    }

    sleep(1);

    for (auto &tid : tids)
        printf("main create a new thread, new thread id is: 0x%lx\n", tid);
   
    for (auto &tid : tids)
    {
        pthread_join(tid, nullptr);
        printf("new thread end...,%lu\n", tid);
    }

    printf("main end...\n");

    return 0; // 如果main函数结束,表示主线程结束,同时也表示,当前进程结束  --- 释放资源
              // 结论是:进程结束,所有线程全部退出,哪怕没有执行完
}

运行结果:

从结果中可以看出来:谁先运行并不确定。这5个进程谁先调度,谁就先运行。

多线程的模板 ------ 就是 for 循环进行创建和等待线程。

3.5 验证线程崩溃问题:

新线程中会一个线程有野指针的问题,但是主线程中是没有问题的:

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>

volatile int gval = 100;

int *data = nullptr; // 定义一个指针

std::string fun()
{
    return "我是另一个函数";
}

// 新线程
void *thread_routine(void *args)
{
    std::string name = static_cast<const char *>(args);
    int cnt = 10;
    while (true)
    {
        printf("new thread is running, name is : %s\n", name.c_str());
        cnt--;
        if (cnt == 3)
        {
            printf("%s is dead...\n", name.c_str());
            int *p = nullptr;
            *p = 0;
        }
        sleep(1);
    }
    return nullptr; // 如果线程执行完了自己的入口函数,表明该线程结束退出
}

int gnum = 5;

int main()
{

    std::vector<pthread_t> tids;
    for (int i = 0; i < gnum; i++)
    {
        pthread_t tid;
        char *p = new char[64]; // 申请一段堆空间
        snprintf(p, 64, "thread-%d", i + 1);

        int n = pthread_create(&tid, nullptr, thread_routine, p);
        (void)n; // 避免警告
        tids.push_back(tid);
    }

    sleep(1);

    for (auto &tid : tids)
        printf("main create a new thread, new thread id is: 0x%lx\n", tid);

    for (auto &tid : tids)
    {
        pthread_join(tid, nullptr);
        printf("new thread end...,%lu\n", tid);
    }

    printf("main end...\n");

    return 0; // 如果main函数结束,表示主线程结束,同时也表示,当前进程结束  --- 释放资源
              // 结论是:进程结束,所有线程全部退出,哪怕没有执行完
}

运行结果:

一个线程崩溃,其它的线程包括主线程都是会崩溃的,证明了线程在运行的时候健壮性降低,线程一旦出现异常,进程就会跟着崩溃,一旦进程崩溃,其它线程全部崩溃。

在执行代码的时候,多线程中,有一个线程触发异常,当前进程会收到信号,其实会把信号发给每一个线程,每一个线程全部都会退,进程退出时,退出信号是segmentation fault。线程退出的导致原因是什么呢?由当前的bash获得当前的进程的退出码得知的,bash不知道是哪一个子进程触发的,但是也不需要知道。

3.6 线程的退出方法:(三个方法)

方法1. return nullptr;

如果线程执行完了自己的入口函数,表明该线程结束退出

exit(10); ( 线程的退出不能使用exit() )

用来进行终止进程的!!任何一个线程,调用exit,都会导致进程退出,变相的导致所有的线程退出!!

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>

void *thread_routine(void *args)
{
    std::string name = static_cast<const char *>(args);
    while (true)
    {
        printf("new thread is running, name is : %s\n", name.c_str());
        sleep(1);
        break;
    }
    // return nullptr; // 如果线程执行完了自己的入口函数,表明该线程结束退出
    exit(10);
}

int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void*)"thread -1");
    (void)n; // 避免警告

    int m = pthread_join(tid, nullptr);
    (void)m;

    printf("main thread end...\n");

    return 0;
}

运行结果:

所以,在线程中是不能用exit的!!!

方法2. pthread_exit()

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>

void *thread_routine(void *args)
{
    std::string name = static_cast<const char *>(args);
    while (true)
    {
        printf("new thread is running, name is : %s\n", name.c_str());
        sleep(1);
        break;
    }
    // return nullptr; // 如果线程执行完了自己的入口函数,表明该线程结束退出
    // exit(10);  //用来进行终止进程的!!任何一个线程,调用exit,都会导致进程退出,变相的导致所有的线程退出!!
    pthread_exit(nullptr);
}

int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void*)"thread -1");
    (void)n; // 避免警告

    int m = pthread_join(tid, nullptr);
    (void)m;

    sleep(3);

    printf("main thread end...\n");

    return 0;
}

新线程能不能知道自己的id是多少??主线程不也是一个线程吗?他是否有线程id呢?

pthread_self(); 自己获取自己的线程id

谁调用整个函数,就返回谁的线程id

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>

void *thread_routine(void *args)
{
    std::string name = static_cast<const char *>(args);
    while (true)
    {
        printf("new thread is running, name is : %s, new thread id: 0x%lx\n", name.c_str(), pthread_self());
        sleep(1);
        break;
    }
    // return nullptr; // 如果线程执行完了自己的入口函数,表明该线程结束退出
    // exit(10);  //用来进行终止进程的!!任何一个线程,调用exit,都会导致进程退出,变相的导致所有的线程退出!!
    // pthread_exit(nullptr);
    return nullptr;
}

int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void*)"thread -1");
    (void)n; // 避免警告

    printf("main thread create done, main thread id: 0x%lx, new thread id: 0x%lx\n", pthread_self(),tid);

    int m = pthread_join(tid, nullptr);
    (void)m;

    sleep(3);

    printf("main thread end...\n");

    return 0;
}

方法3:pthread_cancel(要取消的线程id):可以让一个线程去取消另一个线程

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>

void *thread_routine(void *args)
{
    std::string name = static_cast<const char *>(args);
    while (true)
    {
        printf("new thread is running, name is : %s, new thread id: 0x%lx\n", name.c_str(), pthread_self());
        sleep(1);
        // break;
    }
    // return nullptr; // 如果线程执行完了自己的入口函数,表明该线程结束退出
    // exit(10);  //用来进行终止进程的!!任何一个线程,调用exit,都会导致进程退出,变相的导致所有的线程退出!!
    // pthread_exit(nullptr);
    // return nullptr;
}

int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void*)"thread -1");
    (void)n; // 避免警告

    printf("main thread create done, main thread id: 0x%lx, new thread id: 0x%lx\n", pthread_self(),tid);
    
    sleep(2);
    pthread_cancel(tid);
    printf("cancel target thread...\n");

    int m = pthread_join(tid, nullptr);
    (void)m;

    sleep(3);

    printf("main thread end... m is: %d\n",m);

    return 0;
}

运行结果:

自己可以把自己取消吗??------ 可以的!!

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <pthread.h>

void *thread_routine(void *args)
{
    std::string name = static_cast<const char *>(args);
    while (true)
    {
        printf("new thread is running, name is : %s, new thread id: 0x%lx\n", name.c_str(), pthread_self());
        sleep(1);
        pthread_cancel(pthread_self());
    }
}

int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void*)"thread -1");
    (void)n; // 避免警告

    printf("main thread create done, main thread id: 0x%lx, new thread id: 0x%lx\n", pthread_self(),tid);
    
    sleep(2);
    int m = pthread_join(tid, nullptr);
    (void)m;

    sleep(3);

    printf("main thread end... m is: %d\n",m);

    return 0;
}

运行结果:

结论:

  • 方法1 和 方法2 推荐在自己的线程中使用
  • 方法3 本来就是main取消其它线程的 ---- 最佳实践

4. 线程的传参和返回值

4.1 返回值

4.1.1 从编码层面上理解返回值

第一个:

cpp 复制代码
void *thread_routine(void *args)
{
    std::string name = static_cast<const char *>(args);
    while (true)
    {
        printf("new thread is running, name is : %s, new thread id: 0x%lx\n", name.c_str(), pthread_self());
        sleep(1);
        // pthread_cancel(pthread_self());
        break;
    }

    return (void*)10;  // 数字10在C语言中叫做字面值,类型是:int,占4字节,会被硬编码到代码当中,跟平时定义的字符串是一样的 "hello sy!"
                       // 数字10强转为 void *,充当返回值,就返回出去了
}

第二个:

要获取对应的返回值,想通过函数获取,pthread_join() 第二个参数是一个输出型参数,想通过函数的方式把线程退出时的返回值带出来,返回值是一个 void* 类型,要用函数的方式将返回值带出来:

首先也得有一个 void*

cpp 复制代码
int main()
{
    pthread_t tid;
    int n = pthread_create(&tid, nullptr, thread_routine, (void*)"thread -1");
    (void)n; // 避免警告

    printf("main thread create done, main thread id: 0x%lx, new thread id: 0x%lx\n", pthread_self(),tid);
    
    sleep(2);
    void *ret;
    int m = pthread_join(tid, &ret);
    (void)m;
    if(m == 0)
    {
        printf("new thread return val: %lld\n",(long long)ret);
    }

    sleep(3);

    printf("main thread end... m is: %d\n",m);

    return 0;
}

运行结果:

修改一下返回值:

cpp 复制代码
void *thread_routine(void *args)
{
    std::string name = static_cast<const char *>(args);
    while (true)
    {
        printf("new thread is running, name is : %s, new thread id: 0x%lx\n", name.c_str(), pthread_self());
        sleep(1);
        // pthread_cancel(pthread_self());
        break;
    }
    pthread_exit((void*)20);

}

运行结果:

此时拿到的就是20。

所以创建线程是为了给我完成一件事的,怎么知道事情完成的怎么样了??

cpp 复制代码
void *thread_routine(void *args)
{
    std::string name = static_cast<const char *>(args);
    while (true)
    {
        printf("new thread is running, name is : %s, new thread id: 0x%lx\n", name.c_str(), pthread_self());
        sleep(1);
        // pthread_cancel(pthread_self());
        break;
    }
    pthread_exit((void*)0);  //代码正确执行完
    pthread_exit((void*)1);  //代码执行完,不对的原因
    pthread_exit((void*)2);  //代码执行完,不对的原因
    pthread_exit((void*)3);  //代码执行完,不对的原因
    pthread_exit((void*)4);  //代码执行完,不对的原因
}

不对的原因是什么,此时就可以返回给主线程了。主线程通过pthread_join()就能得知新线程执行的结果:代码跑完,结果对还是不对?

之前进程的退出信息 = 信号 + 退出码,今天pthread_join()返回什么,我们就拿到什么。难道,多线程这里,不考虑所谓的异常吗??

4.1.2 pthread_join() 第二个参数的问题

int pthread_join(pthread_t thread, void **retval);

1. 获取新线程的执行结果,需要考虑新线程的异常问题吗??---- 不用考虑了!!!因为没机会考虑!!!

一旦要等的目标线程内部出现了异常,除0,野指针...线程一旦出现异常,整个进程就全部跟着崩溃了!!主线程 pthread_join() 根本就没有机会返回,所以线程 pthread_join() 不提供所谓获取退出信号的接口。异常属于进程异常,是由父进程关心,通过wait()关心。所以,pthread_join() 考虑常规情况即可。

所以 int pthread_join(pthread_t thread, void **retval); 不再做位图设置。对方给你返回什么,你就拿什么就好了。

2. 新线程是如何把自己的返回值,交给父进程的,父进程如何通过 pthread_join,获得指定子进程的退出信息??

第一讲:

相关推荐
云计算磊哥@10 分钟前
运维开发宝典026-MySQL02数据库表操作
运维·数据库·运维开发
weixin_5231853215 分钟前
Collections.unmodifiableMap详解:真的不可修改吗?
java·linux·前端
黄同学real24 分钟前
解决 Visual Studio Web Deploy 远程发布报 401 未授权 (ERROR\_USER\_UNAUTHORIZED)
服务器
天天进步201541 分钟前
Tunnelto 源码解析 #9:控制服务器设计:Warp、WebSocket、Ping/Pong 与连接保活
运维·服务器·websocket
凡人叶枫1 小时前
Effective C++ 条款04:确定对象被使用前已先被初始化
java·linux·开发语言·c++·嵌入式开发
云栖梦泽1 小时前
玩转RK3506SDK
linux·嵌入式硬件
极客先躯1 小时前
高级java每日一道面试题-2026年02月01日-实战篇[Docker]-Docker Volume 的生命周期管理是怎样的?
java·运维·docker·容器·持久化·架构图·容器卷
Java面试题总结2 小时前
Linux-Ubantu-贴士-apt的地盘
linux·运维·服务器
●VON2 小时前
AtomGit Flutter鸿蒙客户端:数据模型
android·服务器·安全·flutter·harmonyos·鸿蒙
志栋智能2 小时前
超自动化巡检:提升MTTR,缩短业务影响时间
运维·自动化