Linux--线程的控制

目录

0.前言

 1.pthread库

2.关于控制线程的接口

2.1.创建线程(pthread_create)

2.2.线程等待(pthread_join)

代码示例1:

​编辑

***一些问题***

2. 3.创建多线程  

3.线程的终止 (pthread_exit  / pthread_cancel)

总结:

4.线程分离 (pthread_detach)

新线程分离主线程

5.C++ 11中的多线程


0.前言

线程的创建,终止,等待,分离


 1.pthread库

        Linux中有线程吗?没有,只有轻量级进程--(就是线程)。因此Linux下的系统调用只会给用户提供创建轻量级进程的接口,这些接口需要被pthread库进行封装,按照线程的接口提供给用户,用户通过这些接口来创建,终止,等待,分离线程。所以我们称Linux的线程为用户级线程,windows的线程为内核级线程。

 


2.关于控制线程的接口

2.1.创建线程(pthread_create)

引入接口:pthread_create,用于创建一个新线程

参数说明

  • pthread_t *thread:这是一个指向 pthread_t 类型的指针,用于存储新创建的线程的标识符。通过这个标识符,你可以引用或操作这个线程。
  • const pthread_attr_t *attr:这是一个指向 pthread_attr_t 类型的指针,用于设置线程的属性,如堆栈大小、调度策略等。如果传递 NULL,则使用默认属性。
  • void *(*start_routine) (void *):这是新线程将要执行的函数的指针。该函数必须接受一个 void * 类型的参数并返回一个 void * 类型的值。这个函数的参数 arg 将被传递给新线程。(输入一个函数的地址)
  • void *arg:这是传递给 start_routine 函数的参数。

如果成功,pthread_create 返回 0;如果失败,则返回错误码。


2.2.线程等待(pthread_join)

引入接口:pthread_join

参数说明

  • pthread_t thread:这是要等待的线程的标识符(ID),该标识符是由 pthread_create 函数返回的。
  • void **retval:这是一个指向 void * 指针的指针,用于接收被等待线程的返回值。如果被等待的线程调用了 pthread_exit 并传递了一个返回值,或者简单地返回了一个值(对于从 void* 返回类型的线程函数),那么这个值就可以通过这个参数返回给等待的线程。如果对这个返回值不感兴趣,可以传递 NULL

如果成功,pthread_join 返回 0;如果失败,则返回错误码。


代码示例1:

线程的创建和等待:

#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>

void *threadrun(void *args)
{
    int cnt =10;
    while(cnt)
    {
        std::cout<<"new thread run ...,cnt: "<<cnt--<<std::endl;
        sleep(1);
    }
    return nullptr;
}
int main()
{
    pthread_t tid;
    int n = pthread_create(&tid,nullptr,threadrun,(void*)"thread 1");
    
    std::cout<<"main thread join begin..."<<std::endl;
    n= pthread_join(tid,nullptr);

    if(n==0)
    {
        std::cout<<"main thread wait success"<<std::endl;
    }
    return 0;
}

***一些问题***

问题1:mian和new线程谁先运行?不确定

问题2:我们期望谁最后退出?main thread,如何来保证呢?

join来保证,不join呢?会造成类似僵尸进程的问题

问题3:tid是什么样子的?

代码:以16进制的形式打印出来 

std::string PrintToHex(pthread_t &tid)
{
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "0x%lx", tid);
    return buffer;
}
    std::string tid_str = PrintToHex(tid); // 我们按照16进行打印出来
    std::cout << "tid : " << tid_str << std::endl;

这个线程id是一个虚拟地址,后面再谈


问题4:全面看待线程函数传参,它可以传任意类型,当然也可以传类对象的地址,这意味着我们可以给线程传递多个参数,多种方法了

class ThreadData
{
public:
    std::string name;
    int num;
};
void *threadrun(void *args)
{
    //静态强转 
    ThreadData *td = static_cast<ThreadData*>(args);
    int cnt =10;
    while(cnt)
    {
        std::cout << td->name << " run ..." <<"num is: "<<td->num<< ", cnt: " << cnt-- << std::endl;
        sleep(1);
    }
    return nullptr;
}
int main()
{
    ThreadData *td=new ThreadData();
    td->name ="thread-1";
    td->num = 1;
    int n = pthread_create(&tid,nullptr,threadrun,(void*)&td);
}

传类对象的时候最好是在堆上开辟,这样多个线程之间就不会互相干扰。


问题5:pthread_create第三个参数的返回值,该返回值是void*类型的,如果主线程想要获取线程的返回值,可以通过join函数获取(在线程没出错的情况下是能获取到的,如果某一个线程出错,主线程也是会跟着崩掉,因为线程出错误,是直接给整个进程发信号的,导致整个进程都挂掉了)

代码示例:返回一个类对象

#include <iostream>
#include <string>
#include <thread>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>


class ThreadData
{
public:
    int Excute()
    {
        return x + y;
    }
public:
    std::string name;
    int x;
    int y;
    // other
};

class ThreadResult
{
public:
    std::string print()
    {
        return std::to_string(x) + "+" + std::to_string(y) + "=" + std::to_string(result);
    }
public:
    int x;
    int y;
    int result;
};
std::string PrintToHex(pthread_t &tid)
{
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "0x%lx", tid);
    return buffer;
}
void *threadRun(void *args)
{
    ThreadData *td = static_cast<ThreadData*>(args); // (ThreadData*)args
    ThreadResult *result = new ThreadResult();
    int cnt = 10;
    while(cnt)
    {
        sleep(3); 
        std::cout << td->name << " run ..." << ", cnt: " << cnt-- << std::endl;
        result->result = td->Excute();
        result->x = td->x;
        result->y = td->y;
        break;//跑一次退出
    }
    delete td;
    return (void*)result;
}
int main()
{
    pthread_t tid;
    ThreadData *td=new ThreadData();
    td->name="thread-1";
    td->x=10;
    td->y=20;
    int n = pthread_create(&tid, nullptr, threadRun, td);
   
    std::string tid_str = PrintToHex(tid); // 我们按照16进行打印出来
    std::cout << "tid : " << tid_str << std::endl;
  
    std::cout<<"main thread join begin..."<<std::endl;
    
    ThreadResult *result = nullptr; // 开辟了空间的!!!
    n = pthread_join(tid, (void**)&result); 
    if(n == 0)
    {
        std::cout << "main thread wait success, new thread exit code: " << result->print() << std::endl;
    }
    sleep(10);
    return 0;
}


2. 3.创建多线程  

下面是一段示例:

初步:创建线程id和线程name,保存所有线程的id信息,最后主线程回收每个线程

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

const int num = 10;
std::string PrintToHex(pthread_t &tid)
{
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "0x%lx", tid);
    return buffer;
}

void *threadrun(void *args)
{
    std::string name = static_cast<const char*>(args);
    while(true)
    {
        std::cout << name << " is running" << std::endl;
        sleep(1);
        break;
    }
    return args;
}
int main()
{
    std::vector<pthread_t> tids;
    for(int i = 0; i < num; i++)
    {
        // 1. 有线程的id
        pthread_t tid;
        // 2. 线程的名字
        char *name = new char[128];
        snprintf(name, 128, "thread-%d", i+1);
        pthread_create(&tid, nullptr, threadrun, /*线程的名字*/name);
        //3.保存所有线程id
        tids.push_back(tid);
    }
    
    for(auto tid:tids)
    {
        void*name=nullptr;
        pthread_join(tid,&name);
        std::cout<<(const char*)name<<"quit..."<<std::endl;
        delete (const char*)name;
    }

}
​

我们用vector储存线程id集


3.线程的终止 (pthread_exit  / pthread_cancel)

        对于新线程来说,线程终止,函数return;main函数结束,主线程结束,表示整个进程结束!

        关于exit:专门用来终止进程的,不能用来终止线程!任意一个线程调用exit都表示进程终止!如果你想让一个线程马上终止,这里就要用到第三个接口:pthread_exit

参数:

  • retval:这是一个指向任意数据的指针,该数据将被线程的终止状态所使用,并且可以被其他线程通过调用 pthread_join 来访问。

当然你还可以使用接口:pthread_cancel取消一个线程

参数:

  • thread:要发送取消请求的线程标识符(pthread_t 类型)。

代码示例:

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

const int num = 10;
std::string PrintToHex(pthread_t &tid)
{
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "0x%lx", tid);
    return buffer;
}

void *threadrun(void *args)
{
    std::string name = static_cast<const char*>(args);
    while(true)
    {
        std::cout << name << " is running" << std::endl;
        sleep(1);
        break;
    }
    //return args;
    pthread_exit(args);
}
int main()
{
    std::vector<pthread_t> tids;
    for(int i = 0; i < num; i++)
    {
        // 1. 有线程的id
        pthread_t tid;
        // 2. 线程的名字
        char *name = new char[128];
        snprintf(name, 128, "thread-%d", i+1);
        pthread_create(&tid, nullptr, threadrun, /*线程的名字*/name);
        //3.保存所有线程id
        tids.push_back(tid);
    }
    
    for(auto tid:tids)
    {
        void*name=nullptr;
        pthread_join(tid,&name);
        std::cout<<(const char*)name<<"quit..."<<std::endl;
        delete (const char*)name;
    }
    sleep(100);
}

在主线程未退出的情况下,其它线程成功退出了。


线程取消,退出结果为-1; #define PTHREAD_CANCELED ((void *) -1)

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

const int num = 10;
std::string PrintToHex(pthread_t &tid)
{
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "0x%lx", tid);
    return buffer;
}

void *threadrun(void *args)
{
    std::string name = static_cast<const char*>(args);
    while(true)
    {
        std::cout << name << " is running" << std::endl;
        sleep(1);
    }
}
int main()
{
    std::vector<pthread_t> tids;
    for(int i = 0; i < num; i++)
    {
        // 1. 有线程的id
        pthread_t tid;
        // 2. 线程的名字
        char *name = new char[128];
        snprintf(name, 128, "thread-%d", i+1);
        pthread_create(&tid, nullptr, threadrun, /*线程的名字*/name);
        //3.保存所有线程id
        tids.push_back(tid);
    }
    
    sleep(5);
    for(auto tid : tids)
    {
        pthread_cancel(tid); // 取消
         std::cout << "cancel: " << PrintToHex(tid) << std::endl;
        void *result = nullptr; // 线程被取消线程的退出结果是:-1 #define PTHREAD_CANCELED ((void *) -1)
        pthread_join(tid, &result);
        std::cout << (long long int)result << " quit..." << std::endl;

    }
    sleep(100);
}


总结:

    新线程如何终止?

    1. 线程函数 return

    2. pthread_exit

    3. main thread call pthread_cancel, 新线程退出结果是-1


4.线程分离 (pthread_detach)

        线程分离的是将线程与创建它的进程(或主线程)的终止状态分离。当一个线程被分离后,它依然属于进程内部,但它不再需要被其他线程显式地等待(通过 pthread_join)来释放其资源。当分离的线程终止时,它的所有资源会自动被释放回系统,无需其他线程的干预。

参数

  • thread:要分离的线程的标识符(pthread_t 类型)。

返回值

  • 成功时返回 0。
  • 失败时返回错误号。

        一个线程被创建,默认是joinable,必须要被join的;如果一个线程被分离,线程的工作状态分离状态,不需要/不能被join的。

        这里我们还需要借助一个接口:pthread_self,一调用就是获取自己的线程id


新线程分离主线程

代码示例:一旦分离主线程就不能等待了,如果等待会发生什么?这里我们看一下分离且join后,join的返回值

        我们发现返回值为:22,这说明主线程以等待就直接出错了。所以主线程无需等待,主线程可以做自己的事情了。如果在线程分离的情况下,且主线程没有做等待,新线程出错了,整个进程也是直接挂掉的,因为它还是在进程内部。

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

const int num = 10;
std::string PrintToHex(pthread_t &tid)
{
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "0x%lx", tid);
    return buffer;
}

void *threadrun(void *args)
{
    pthread_detach(pthread_self());
    std::string name = static_cast<const char*>(args);
    while(true)
    {
        std::cout << name << " is running" << std::endl;
        sleep(1);
    }
   pthread_exit(args);
}
int main()
{
    std::vector<pthread_t> tids;
    for(int i = 0; i < num; i++)
    {
        // 1. 有线程的id
        pthread_t tid;
        // 2. 线程的名字
        char *name = new char[128];
        snprintf(name, 128, "thread-%d", i+1);
        pthread_create(&tid, nullptr, threadrun, /*线程的名字*/name);
        //3.保存所有线程id
        tids.push_back(tid);
    }
    
    sleep(5);
    for(auto tid : tids)
    {
         std::cout << "cancel: " << PrintToHex(tid) << std::endl;
        void *result = nullptr; // 线程被取消线程的退出结果是:-1 #define PTHREAD_CANCELED ((void *) -1)
        int n = pthread_join(tid, &result);
        std::cout << (long long int)result << " quit...n :" <<n<< std::endl;

    }
    sleep(100);
}


5.C++ 11中的多线程

        C++11在Linux中使用多线程,编译时也是要链接pthread库,因为C++11中的多线程本质,就是对原生线程库接口的封装!!!

#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

void threadrun(std::string name, int num)
{
    while(num)
    {
        std::cout << name << " num : " << num<< std::endl;
        num--;
        sleep(1);
    }
}

int main()
{
    std::string name = "thread-1";
    std::thread mythread(threadrun, std::move(name), 10);
    while(true)
    {
        std::cout << "main thhread..." << std::endl;
        sleep(1);
    }
    mythread.join();
    return 0;
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/786670.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用LangGraph、 Ollama,构建个人的 AI Agent

如果你还记得今年的 Google I/O大会&#xff0c;你肯定注意到了他们今年发布的 Astra&#xff0c;一个人工智能体&#xff08;AI Agent&#xff09;。事实上&#xff0c;目前最新的 GPT-4o 也是个 AI Agent。 现在各大科技公司正在投入巨额资金来创建人工智能体&#xff08;AI …

逻辑回归模型(非回归问题,而是解决二分类问题)

目录&#xff1a; 一、Sigmoid激活函数&#xff1a;二、逻辑回归介绍&#xff1a;三、决策边界四、逻辑回归模型训练过程&#xff1a;1.训练目标&#xff1a;2.梯度下降调整参数&#xff1a; 一、Sigmoid激活函数&#xff1a; Sigmoid函数是构建逻辑回归模型的重要激活函数&am…

C++ | Leetcode C++题解之第221题最大正方形

题目&#xff1a; 题解&#xff1a; class Solution { public:int maximalSquare(vector<vector<char>>& matrix) {if (matrix.size() 0 || matrix[0].size() 0) {return 0;}int maxSide 0;int rows matrix.size(), columns matrix[0].size();vector<…

Java基础语法--基本数据类型

Java基础语法–基本数据类型 Java是一种静态类型语言&#xff0c;这意味着每个变量在使用前都必须声明其数据类型。Java提供了多种基本数据类型&#xff0c;用于存储整数、浮点数、字符和布尔值等。以下是Java中的基本数据类型及其特点&#xff1a; 1. 整型&#xff08;Integ…

Java面试八股之MySQL中的MVCC是什么,作用是什么?

MySQL中的MVCC是什么&#xff0c;作用是什么&#xff1f; MySQL中的MVCC&#xff08;Multiversion Concurrency Control&#xff0c;多版本并发控制&#xff09;是一种并发控制机制&#xff0c;用于提高数据库的并发性能并确保数据的一致性&#xff0c;特别是在高并发读写场景…

python调用qt编写的dll

报错&#xff1a;FileNotFoundError: Could not find module F:\pythonProject\MINGW\sgp4Lib.dll (or one of its dependencies). Try using the full path with constructor syntax. 只有两种情况&#xff1a; 1.路径不对 2.库的依赖不全 1、如果是使用了qt库的&#xff0…

使用tkinter拖入excel文件并显示

使用tkinter拖入excel文件并显示 效果代码 效果 代码 import tkinter as tk from tkinter import ttk from tkinterdnd2 import TkinterDnD, DND_FILES import pandas as pdclass ExcelViewerApp(TkinterDnD.Tk):def __init__(self):super().__init__()self.title("Excel…

unity 手动制作天空盒及使用

提示&#xff1a;文章有错误的地方&#xff0c;还望诸位大神不吝指教&#xff01; 文章目录 前言一、使用前后左右上下六张图1.准备6张机密结合的图片2.创建Material材质球3.使用天空盒 二、使用HDR贴图制作1.准备HDR贴图2.导入unity 修改Texture Sourpe 属性3.创建材质球4.使用…

自定义刷题工具-python实现

背景&#xff1a; 最近想要刷题&#xff0c;虽然目前有很多成熟的软件&#xff0c;网站。但是能够支持自定义的导入题库的非常少&#xff0c;或者是要么让你开会员&#xff0c;而直接百度题库的话&#xff0c;正确答案就摆在你一眼能看见的地方&#xff0c;看的时候总觉得自己…

使用Python绘制QQ图并分析数据

使用Python绘制QQ图并分析数据 在这篇博客中&#xff0c;我们将探讨如何使用Python中的pandas库和matplotlib库来绘制QQ图&#xff08;Quantile-Quantile Plot&#xff09;&#xff0c;并分析数据文件中的内容。QQ图是一种常用的统计图表&#xff0c;用于检查一组数据是否服从…

A股探底回升,2900点强势支撑,3000点还会远吗?

今天的A股探底回升&#xff0c;姿势绝了&#xff01;具体原因是这样的&#xff0c;盘面上出现2个重要变化&#xff0c;一起来看看&#xff1a; 1、今天两市低开高走&#xff0c;一度回踩2904点&#xff0c;然后筑底反弹&#xff0c;究竟是昙花一现还是迎来大反击&#xff1f; …

深度学习-数学基础(四)

深度学习数学基础 数学基础线性代数-标量和向量线性代数-向量运算向量加和向量内积向量夹角余弦值 线性代数-矩阵矩阵加法矩阵乘法矩阵点乘矩阵计算的其他内容 人工智能-矩阵的操作矩阵转置&#xff08;transpose&#xff09;矩阵与向量的转化 线性代数-张量&#xff08;tensor…

1001-04SF 同轴连接器

型号简介 1001-04SF是Southwest Microwave的2.92 mm连接器。该连接器采用多种材料制成&#xff0c;包括不锈钢、黄铜和硅橡胶等&#xff0c;以确保其性能和耐用性。它可以帮助工程师和制造商进行设计和制造&#xff0c;并确保连接器的性能和质量。 型号特点 电缆螺母&#xff…

【中项第三版】系统集成项目管理工程师 | 第 4 章 信息系统架构④ | 4.7

前言 第4章对应的内容选择题和案例分析都会进行考查&#xff0c;这一章节属于技术相关的内容&#xff0c;学习要以教材为准。本章分值预计在4-5分。 目录 4.7 安全架构 4.7.1 安全威胁 4.7.2 定义与范围 4.7.3 整体架构设计 4.7.4 网络安全架构设计 4.7.5 数据库系统安…

【Python基础】代码如何打包成exe可执行文件

本文收录于 《一起学Python趣味编程》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、安装PyInstaller三、使用PyInstaller打包四、验证打包是否成功五、总结 一、前言 本文介绍如何…

西安电子科技大学811/821/833/834/953小班(8人)授课上线

你还在为自己努力&#xff0c;而成绩上不去而烦恼嘛&#xff1f;来看看下面这个小班介绍&#xff0c;或许你会感兴趣&#xff01; 01、西电研梦现在开设一个8人小班&#xff0c;什么是8人小班授课呢&#xff1f; 小班采用82授课机制&#xff1b;其中8人指&#xff1a;8位考研…

活动回顾|2024 MongoDB Developer Day圆满收官!

上周六&#xff0c;MongoDB专家与团队在深圳 与90位开发者度过了充实一日 至此&#xff0c;2024 MongoDB Developer Day 北上深三站之行全部圆满结束&#xff01; 一文回顾本次活动全程与精彩影像&#xff01; MongoDB Developer Day 专为开发者定制的技术盛宴 全天沉浸动手实…

【LabVIEW学习篇 - 5】:数据类型——数值、字符串

文章目录 数值枚举下拉列表控件 字符串字符串与十六进制截取字符串连接字符串 字符串与数值间的转换字符串转为数值数值转为字符串 数值 如下图所示&#xff0c;各种数值型数据的不同之处在于存储和表示数据时所使用的位置不同。 浮点型 整型 在LabVIEW中&#xff0c;想要改…

pointnet2_ops_lib/.安装报错解决方案

问题 3D点云相关的代码例如pointnn、pointmlp都需要安装pointnet2_ops&#xff0c;可是基本上在安装pointnet2_ops时总会报错&#xff0c;终归原因是虚拟环境的cuda版本和安装的torch&#xff0c;torchvision&#xff0c; torchaudio版本不一致导致。 方案 这里以pointmlp&am…

原来Kimi不是不作为,而是在准备大招!

月之暗面Kimi 作为一款我每天都在使用的AI智能助手&#xff0c;是真正的帮助我解决了不少工作及日常创作的一些事情。 它的表现能力也是毋庸置疑&#xff0c;不论是业内还是普通人&#xff0c;10个人当中可能就有9个人在使用Kimi。 而昨天&#xff08;7月8日&#xff09;Kimi…