Linux--线程的控制

news2024/11/15 10:24:27

目录

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;
}

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python—读写csv文件

目录 csv库方法参数 读取数据 csv.reader方法 文件指定行或列数据读取操作 txt文件的readlines、read方法 csv.DictReader方法 写入数据 txt文件的write&#xff0c;writelines csv.writer方法 csv.DictWriter方法 读写联合(修改及插入数据) 读写csv 文件时&#xf…

语义言语流畅性的功能连接和有效连接

摘要 语义言语流畅性(SVF)受损在多种神经系统疾病中都存在。虽然已经报道了SVF相关区域的激活情况&#xff0c;但这些区域如何相互连接以及它们在脑网络中的功能作用仍存在分歧。本研究使用功能磁共振成像评估了健康被试SVF静态和动态功能连接(FC)以及有效连接。观察到额下回(…

c++初阶学习----入门(上)

大家好啊。最近学习了一点关于c的知识。这不就迫不及待的来与大家分享了嘛。但我这也是现学现卖所以咧。有很多遗落甚至不对的地方希望大家可以在评论区里面指出来。这样也可以增加大家对知识的巩固。 c语言与c的联系 不知道大家看到c会不会不由自主的联想到C语言啊。毕竟都是…

TVBox的Json配置接口编写指南,模板格式说明(如何打造一个专属于自己的TVBox配置文件)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 配置解析 📒📝 JSON基础📝 配置文件结构/参数说明📝 编写步骤📝 注意事项🎈 接口分享⚓️ 相关链接 ⚓️📖 介绍 📖 TVBox 是一款备受欢迎的电视盒子应用(免费影视必备),它以其高度自定义的特性深受用户喜爱…

Pearson 相关系数的可视化辅助判断和怎么用

Pearson 相关系数的可视化辅助判断和怎么用 flyfish Pearson 相关系数 是一种用于衡量两个连续型变量之间线性相关程度的统计量。其定义为两个变量协方差与标准差的乘积的比值。公式如下&#xff1a; r ∑ ( x i − x ˉ ) ( y i − y ˉ ) ∑ ( x i − x ˉ ) 2 ∑ ( y i −…

jitsi 使用JWT验证用户身份

前言 Jitsi Meet是一个很棒的会议系统,但是默认他运行所有人创建会议,这样在某种程度上,我们会觉得他不安全,下面我们就来介绍下使用JWT来验证用户身份 方案 卸载旧的lua依赖性sudo apt-get purge lua5.1 liblua5.1-0 liblua5.1-dev luarocks添加ubuntu的依赖源,有则不需…

AI时代算法面试:揭秘高频算法问题与解答策略

三种决策树算法的特点和区别 ID3算法&#xff1a;基本的决策树算法&#xff0c;适用于简单的分类问题C4.5算法&#xff1a;改进了ID3算法&#xff0c;适用于更复杂的分类问题&#xff0c;可以处理连续型数据和缺失值CART算法&#xff1a;更加通用的决策树算法&#xff0c;适用于…

住宅代理、移动代理和数据中心代理之间的区别

如果您是一名认真的互联网用户&#xff0c;可能需要反复访问某个网站或服务器&#xff0c;可能是为了数据抓取、价格比较、SEO 监控等用例&#xff0c;而不会被 IP 列入黑名单或被 CAPTCHA 阻止。 代理的工作原理是将所有传出数据发送到代理服务器&#xff0c;然后代理服务器将…

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

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

VBA实现Excel的数据透视表

前言 本节会介绍通过VBA的PivotCaches.Create方法实现Excel创建新的数据透视表、修改原有的数据透视表的数据源以及刷新数据透视表内容。 本节测试内容以下表信息为例 1、创建数据透视表 语法&#xff1a;PivotCaches.Create(SourceType, [SourceData], [Version]) 说明&am…

面对数据不一致性的解决方案:

polarDB是读写分离和计算存储分离的分布式数据库&#xff0c;并且副本的log replicate是基于Parallel-Raft协议来实现的。所以在瞬时进行写和读的操作时&#xff0c;是不可避免会存在数据一致性问题&#xff0c;导致这个数据一致性问题的原因不是事务&#xff0c;而是多副本日志…

【考研数学】李林《880题》25版听说大改版?和和24版差别大吗?

25版和24版总体差别不大&#xff0c;只有小部分内容有所变动&#xff01; 拓展题部分的更新&#xff1a;25版在拓展题部分进行了一些更新&#xff0c;从李林的模拟题中挑选了大约40道题目添加到新版中。 高等数学&#xff1a;变动主要集中在前三章&#xff0c;但具体的题目变…

【C++】开源:坐标转换和大地测量GeographicLib库配置使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍坐标转换和大地测量GeographicLib库配置使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关…

Facebook社交平台的未来发展趋势分析

随着科技和社交需求的不断演变&#xff0c;Facebook作为全球最大的社交平台之一&#xff0c;其未来发展的趋势备受关注。从技术创新到社会影响&#xff0c;Facebook正在经历着前所未有的变化和挑战。本文将探讨Facebook未来发展的几个关键趋势&#xff0c;并分析其可能的影响和…

SpringBoot 实现视频分段播放(通过进度条来加载视频)

需求&#xff1a;现在我本地电脑中有一个文件夹&#xff0c;文件夹中都是视频&#xff0c;需要实现视频播放的功能。 问题&#xff1a;如果通过类似 SpringBoot static 文件夹的方式来实现&#xff0c;客户端要下载好完整的视频之后才可以播放&#xff0c;并且服务端也会占用大…

Androidstudio开发,天气预报APP

1.项目功能思维导图 2. 项目涉及到的技术点 数据来源&#xff1a;和风天气API使用okhttp网络请求框架获取api数据使用gson库解析json数据使用RecyclerViewadapter实现未来7天列表展示和天气指数使用PopupMenu 实现弹出选项框使用动画定时器实现欢迎页倒计时和logo动画使用Text…

用Vue3和Plotly.js绘制交互式3D散点图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 使用 Plotly.js 创建 2D 密度图 应用场景介绍 密度图是一种可视化数据分布的图表&#xff0c;它显示了数据点的密度在不同区域的变化情况。在许多科学和工程领域中&#xff0c;密度图被广泛用于探索和分析数据…

java项目总结数据库

1.什么是数据库 用于存储和管理数据的仓库 2.数据库的特点 1.持久化存储数据。确实数据库就是一个文件系统。 2.便于存储和管理数据 3.使用统一的方式操作数据库 --SQL 3.MqSql服务启动 4.登录和退出 这里的ip值IP地址 5.客户端与服务器关系 6.目录结构 7.SQL 1.什么是SQL&…

AI赋能OFFICE 智能化办公利器!

ONLYOFFICE在线编辑器的最新版本8.1已经发布&#xff0c;整个套件带来了30多个新功能和432个bug修复。这个文档编辑器无疑成为了办公软件中的翘楚。它不仅支持处理文本文档、电子表格、演示文稿、可填写的表单和PDF&#xff0c;还允许多人在线协作&#xff0c;并支持AI集成&…

10、Python之写出更加Pythonic的代码:unpacking拆包机制

引言 有些小伙伴有其他编程语言的学习、使用的经验&#xff0c;然后迁移到Python。一般会比完全的新手小白&#xff0c;更快速地把Python用起来。这是他们的优势&#xff0c;但也是他们的劣势。 之所以这么说&#xff0c;是因为从其他编程语言带过来的&#xff0c;除了相通的编…