我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++初级线程管理

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C++初级线程管理

前言:

实际程序运行时,每个程序都有一个程序入口,线程也不例外,使用线程时,需要给线程提供一个入口函数,线程执行完入口函数时,线程将退出。C++11中提供了std::thread库,本文将从线程的启动、线程等待、线程分离、线程传参、线程识别等几个方面介绍初级线程管理的知识。

1 线程启动

C++11中,线程的启动终究是对std::thread的对象进行构造。

线程构造的类别如下:

1.1  线程函数无参数无返回值

此类可以说是最简单的线程启动,函数不需要传参也不需要返回函数执行结果,执行完成后,线程自动退出。

形如:


void FunDoingNothing();
std::thread(FunDoingNothing)


编写代码时,需要加上<thread>头文件以方便编译器能够正确处理thread对象。

1.2  线程函数有参数无返回值

C+=11中,thread的构造函数中使用了可变参数,这样,可以使得构造thread对象时可以自定义传入参数,

构造函数的定义如下:


template<class F, class... Args> explicit thread(F&& f, Args&&... args);


在实际使用时,线程函数有参数时可以定义形式如下:


void printMsg(int a, int b) {
  cout << "input params are:" << a <<","<<b<< endl;
}
std::thread my_thread(printMsg, 3, 4)


1.3  调用可调用的类型构造

使用时,可以将带有执行函数的变量传入thread的构造函数中从而替换默认的构造函数,

如下:


using namespace std;
class BackGroundTask{ 
public:
    void operator()() const{
        doSomeThing();
    }
priavte:
    doSomeThing();
};
int main(){
    BackGroundTask f;
    std::thread myThread(f);
}


上面的代码中,在启动线程时同构构造对象f,f对象的重载函数中调用了线程运行时要执行的方法。但有一点需要注意的是,在传入临时的构造对象时,不经过处理,可能会让编译器产生错误的理解。

如:


std::thread myThread(BackGroundTask());


这里相当与声明了一个名为myTread的函数, 这个函数带有一个参数(函数指针指向没有参数并返回BackGroundTask对象的函数), 返回一个 std::thread 对象的函数, 而非启动了一个线程。

如果要解决这个问题,只需要如下处理即可:


std::thread myThread((BackGroundTask()));
std::thread myThread{BackGroundTask()};


当然,也可以使用lamda表达式实现上述功能,如下:


std::thread myThread([]{
  doSomeThing();
  });


2 等待线程

C++11中,确保线程执行完后,主线程在退出,需要在代码中使用join()函数,这样就可以保证变量在线程结束时才会进行销毁。

2.1 join等待

在实际编程时,join函数只是简单的等待或者不等待。在有些场景下就会不使用,如果想要进行更加灵活的控制,需要使用C++11中提供的其他机制,这个也会在后面的推文中进行说明。
在编程时,如果对一个线程使用了join,那么在后续的操作中如果使用joinable()执行结果将返回false。既一旦使用了join。线程对象将不能重复使用。如下代码中,在线程中使用join等待。


class BackGroundTask
{ 
public:
    void operator()()
{
        doSomeThing();
    } 
private:
    void doSomeThing() {cout<<"线程退出"<<endl;};
};
int main()
{
    BackGroundTask f;
    std::thread myThread(f);
    myThread.join();
    cout<<"退出"<<endl;
}


上面的代码使用了线程等待,可以输出正确的结果,如下:

线程退出
退出

如果将 myThread.join()语句注释,再次执行时,程序将执行出错,因为在子线程还没有结束时,主线程已经结束。

运行结果如下:

退出
terminate called without an active exception

上面的输出具备不确定性,代码运行时结果随机。

2.2 异常场景的join等待

异常场景中,如果没有充分考虑join的位置,就可能会产生因为异常导致主线程先于子线程退出的情况,解决这些问题可以通过下面两种方法进行处理:

2.2.1  通过异常捕获

通过分析代码中的异常场景,对异常使用try...catch进行捕获,然后在需要线程等待的地方调用join()函数,这种方法虽然可以轻易地捕获问题并对问题进行修复,但并非是通用法则,还需要根据实际情况进行分析。如检查并确认是否线程函数中是否使用了局部变量的引用等其它原因。

2.2.2 使用RAII方式进行线程等待

RAII可以理解为资源获取既初始化。因为全写为:Resource Acquisition Is Initialization
实际使用时,通过定义一个类,然后在析构函数中使用join函数进行线程等待。这样可以避免场景有遗漏的地方。


class thread_guard
{
private:
    std::thread& t;
public:
    explicit thread_guard(std::thread& t_):t(t_){}
    ~thread_guard()
    {
        if(t.joinable())
        {
            t.join();
        }
    } 
    thread_guard(thread_guard const&)=delete;
    thread_guard& operator=(thread_guard const&)=delete;
};


 如上,通过在将线程对象传入到类thread_guard中,如果thread_guard类对象的局部变量被销毁,则在析构函数中会将线程托管到原始线程。
thread_guard中,使用delete标识,禁止生成该类的默认拷贝构造、以及赋值函数。
在实际编程时如果不想线程等待,可以使用detach方法,将线程和主线程进行分离。

3 线程分离

线程分离使用detach方法,使用后将不能在对已分离的线程进行管理,但是分离的线程可以真实的在后台进行运行。当线程退出时,C++会对线程资源进行清理和回收。
线程分离通常被用作守护线程或者后台工作线程。

使用方法如下:


int main()
{
    BackGroundTask f;
    std::thread myThread(f);
    myThread.detach();
    cout<<"退出"<<endl;
}


4 向线程传递参数

向线程传递参数非常简单,在上面的代码中也有提及,这里主要说下向线程中传递参数的陷阱。

看下面的代码:


void f(int i,std::string const& s);
void oops(int some_param)
{
char buffer[1024]; 
sprintf(buffer, "%i",some_param);
std::thread t(f,3,buffer);
t.detach();
}


上面的代码中buffer是一个局部指针变量,使用后,可能会导致线程出现未定义的行为,因为从char*到string的转换时使用的是隐式转换,但是thread在使用时会将变量拷贝到线程私有内存,但是并不知道需要将参数进行转换,因此复制到私有内存的变量就没有转换成期望的对象。
如果要解决这个问题,可以在使用时直接将参数类型转换成函数默认的类型,在上面的例子中可以

做如下操作:


std::thread t(f,3,std::string(buffer));


但是这样做依然存在问题,既线程在复制变量到私有内存时,只复制了变量值,这样在线程调用后,如果继续使用线程函数处理后的变量时可能变量并没有改造,依旧是线程调用之前的变量。
因此要想在函数传参过程中使得线程拷贝时依旧保持引用,可以在线程调用时使用引用方式,

如:


std::thread t(f,3,std::ref(std::string(buffer)));


5 线程识别

每个线程都有一个线程标识,在C++11中,线程标识通过std::thread::id进行标识,std::thread::id可以复用并进行比较,如果两个线程的id相等,那么它们就是同一个线程或者没有线程,如果不等就表示两个是不同的线程或者其中一个线程不存在。

线程id的获取方法有两种,如下:

5.1 thread成员函数获取

通过std::thread::get_id()可以获取线程的id。

使用方法如下:


int main()
{
    BackGroundTask f;
    std::thread myThread(f);
    cout<<"线程id:"<<myThread.get_id()<<endl;
    myThread.detach();
    cout<<"退出"<<endl;
}


线程运行结果为:

  • 线程id:139879559096064
  • 退出

5.2 std::this_thread::get_id()

线程id可以用来区分主线程和子线程,通过std::this_thread::get_id()可以先将主线程id保存,然后在和子线程进行比较,从而区分主线程和子线程。

代码如下:


int main()
{
    std::thread::id master_thread=std::this_thread::get_id();
    BackGroundTask f;
    std::thread myThread(f);
    if(master_thread!=myThread.get_id())
    {
        cout<<"子线程id:"<<myThread.get_id()<<endl;
    }
    myThread.detach();
    cout<<"退出"<<endl;
}


代码中,先保存了主线程的id标识,然后获取子线程id,比较两个线程id。如果不相等则输出子线程id。

代码运行结果如下:

子线程id:140161423791872

到此这篇关于C++初级线程管理的文章就介绍到这了,更多相关C++线程管理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++初级线程管理

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++初级线程使用方法有哪些

这篇文章主要讲解了“C++初级线程使用方法有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++初级线程使用方法有哪些”吧!1 线程启动C++11中,线程的启动终究是对std::thre
2023-06-22

C++线程怎么创建和管理

C++中创建和管理线程的主要方式是使用std::thread类。以下是创建和管理线程的基本步骤:包含头文件:首先需要包含头文件。创建线程:使用std::thread类的构造函数创建线程。构造函数接受一个可调用的对象作为参数,可以是函数、函数
2023-10-26

C++ 内存管理中的线程安全

c++++ 中的线程安全内存管理通过确保多个线程同时访问共享数据时不会出现数据损坏或竞争条件,来保证数据完整性。关键要点:使用 std::shared_ptr 和 std::unique_ptr 等智能指针实现线程安全的动态内存分配。使用互
C++ 内存管理中的线程安全
2024-05-02

操作系统:用户级线程与内核级线程——探析进程管理的奥秘

用户级线程和内核级线程是操作系统进程管理中的两种重要概念,它们在实现并发编程方面有着不同的原理和应用场景。本文将对这两种线程进行深入探讨,并通过演示代码来说明它们的差异和优缺点。
操作系统:用户级线程与内核级线程——探析进程管理的奥秘
2024-02-03

C++ 中如何创建和管理线程?有哪些线程同步机制?

c++++ 中的线程线程是轻量级的执行单元,可实现并发编程。使用 std::thread 类创建线程,并通过互斥锁、条件变量和自旋锁等同步机制维护共享数据的一致性。实战案例展示了使用线程并发计算求和的过程。C++ 中的线程创建与管理线程是
C++ 中如何创建和管理线程?有哪些线程同步机制?
2024-05-07

C#线程优先级是什么

本篇内容主要讲解“C#线程优先级是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C#线程优先级是什么”吧!C#线程优先级的必要性:如果在应用程序中有多个线程在运行,但一些线程比另一些线程重要
2023-06-17

Android线程管理之ActivityThread

ActivityThread功能 它管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),并根据AMS的要求(通过IApplicationThread接口,AMS为Client、ActivityThread.Applica
2022-06-06

软考初级网络管理员报名费用

  软考初级网络管理员报名费用在单科50-80多不等,原则上网上缴费成功后不再办理退费。编程学习网小编将全国各地区软考初级网络管理员报名费用进行了汇总整理,详见正文。  软考初级网络管理员报名费用由各地自行安排,各地软考初级网络管理员报名费用在单科50-80多不等。为方便考生直观的了解到各地区网络管理员报名费的问题,编程学习网
软考初级网络管理员报名费用
2024-04-19

软考网络管理员是初级职称吗

  软考网络管理员能评初级职称,通过软考获得证书的人员,表明其已具备从事相应专业岗位工作的水平和能力,用人单位可根据工作需要从获得证书的人员中择优聘任相应专业技术职务。  考取了软考初级证书,就具备了评初级职称的相应资格。根据原人事部、原信息产业部颁布的《关于印发〈计算机技术与软件专业技术资格(水平)考试暂行规定〉和〈
软考网络管理员是初级职称吗
2024-04-19

软考初级网络管理员考试题型

软考初级网络管理员考试的题型设计旨在全面评估考生在网络管理领域的理论知识和实践技能。客观题着重于基础概念和原则的考核,而主观题则更侧重于考查考生的分析、解决问题和应用能力。具体请见下文。
软考初级网络管理员考试题型
2024-08-06

软考初级网络管理员总分多少分

软考初级网络管理员考试作为信息产业部和人事部联合举办的国家级考试,其成绩构成和评分标准具有一定的特点,了解考试的总分和评分标准是每位考生都必须掌握的信息。具体请见下文。
软考初级网络管理员总分多少分
2024-08-06

软考初级网络管理员考多长时间

软考初级网络管理员考试的时长由两个科目组成,分别是基础知识和应用技术,这两个科目通常是连续进行的,总考试时长为240分钟,即4个小时,考生可提前交卷。具体请见下文。
软考初级网络管理员考多长时间
2024-08-06

软考初级网络管理员考试是机考吗

软考初级网络管理员考试自改为机考以来,考生需要在计算机上完成所有题目。这种考试形式不仅要求考生具备扎实的专业知识,还需要熟悉计算机操作和机考系统的使用。具体请见下文。
软考初级网络管理员考试是机考吗
2024-08-06

软考初级网络管理员考试难度大吗

软考初级网络管理员考试是中国计算机技术与软件专业技术资格(水平)考试体系中的一项,旨在测试考生在网络管理领域的基础知识和技能。软考初级网络管理员考试难度大吗?具体请见下文。
软考初级网络管理员考试难度大吗
2024-08-06

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录