C++ 多线程之初识多线程

简介: 这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。

写在前面:

是假老练与C扎扎还是假老练与风车车呢, 但是这个好像貌似不太重要, 重要的是下面的正文, 嘻嘻~~~

1. 什么是进程

1.1 操作系统资源调度的基本单位

计算机中的程序关于数据集合上的一次运行活动。一个运行中的程序被称为一个进程(必须是运行中的程序)。

1.2 进程的特性:

1. 动态性

进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。

2. 并发性

任何进程都可以同其他进程一起并发执行。

3. 独立性

进程是一个能独立运行的基本单位,同时也是操作系统分配资源和调度的独立单位。

4. 结构性

进程由程序、数据和进程控制块三部分组成。

5. 异步性

由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进。

2. 什么是线程

2.1 操作系统调度的基本单位

每个进程都有一个主线程,并且主线程唯一,线程就是一个代码的运行通道。

3. 什么是并发

3.1 并发

一个程序同时执行多个独立的任务,并发的主要目的是提高性能(同时做多个事情)。单核CPU,某一时刻只能执行一个任务, 有操作系统调度,每秒执行多次所谓的“任务切换”,实现并发的假象。而且上下文切换需要时间开销(比如操作系统要保存你切换时的各种状态,变量或状态的存储,执行进度信息,都需要时间开销)。多核CPU,如果任务数小于核数,可以实现真正意义上的并发(硬件并发)

3.2 并发的实现

3.2.1 多个进程实现并发

主要解决进程间通信问题:
同主机,主要使用管道,文件,消息队列,内存共享实现
不同主机,网络编程实现(Socket)

3.2.2 单进程,多个线程实现并发

单个进程中,创建了多个线程,每个线程都有自己独立的运行路径,但是一个进程中所有线程共享地址空间( 一个进程中所有线程共享内存空间),例如:全局变量,全局指针,全局引用都可以在线程之间传递。所以使用多线程开销远远小于多进程。共享内存带来的问题,数据一致性问题(多个线程都要往一块内存空间存储数据,造成资源竞争(可以使用锁解决临界数据脏的问题))。

4. C++线程的创建

4.1 使用的头文件

#include <thread>

4.2 主线程先于子线程结束造成的问题

直接创建线程不做处理会造成**编译器调用abort终止我们的程序**

#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;

void print() {
    int count = 2;
    int m = 0;
    while (count--) {
        cout << "主线程执行: m = "<< m++ << endl;
        Sleep(1000);
    }
}
void printData() {
    int n = 0;
    while (1) {
        cout << "子线程执行: n = "<< n++ << endl;
        Sleep(1000);
    }
}
int main() {

    thread t1(printData);   //创建子线程
    print();                //主线程执行
    return 0;
}

4.3 join的使用

加入/汇合线程,阻塞主线程(主线程等待子线程结束)

#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;

void print() {
    int count = 2;
    int m = 0;
    while (count--) {
        cout << "主线程执行: m = "<< m++ << endl;
        Sleep(1000);
    }
}
void printData() {
    int count = 5;
    int n = 0;
    while (count--) {
        cout << "子线程执行: n = "<< n++ << endl;
        Sleep(1000);
    }
}
int main() {

    thread t1(printData);
    print();
    t1.join();
    cout << "主线程结束------" << endl;
    return 0;
}

主线程会等待子线程结束

4.4 detach的使用

分离 驻留后台(子线程后台执行)

#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;

void print() {
    int count = 2;
    int m = 0;
    while (count--) {
        cout << "主线程执行: m = "<< m++ << endl;
        Sleep(1000);
    }
}
void printData() {
    int count = 5;
    int n = 0;
    while (count--) {
        cout << "子线程执行: n = "<< n++ << endl;
        Sleep(1000);
    }
}
int main() {

    thread t1(printData);
    print();
    t1.detach();
    cout << "主线程结束------" << endl;
    return 0;
}

子进程会在后台执行

注: 对于同一个线程,join 和detach只能存在一个

当两者同时存在同一个线程时:

4.5 joinable的使用

判断当前线程是否可以被join与detach ,如果可以返回true,不可以返回false

#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;

void print() {
    int count = 2;
    int m = 0;
    while (count--) {
    cout << "主线程执行: m = "<< m++ << endl;
        Sleep(1000);
    }
}
void printData() {
    int count = 10;
    int n = 0;
    while (count--) {
        cout << "子线程执行: n = "<< n++ << endl;
        Sleep(1000);
    }
}
int main() {

    thread t1(printData);
    print();
    if (t1.joinable())
        cout << "t1可以被join" << endl;
    else
        cout << "t1不能被join" << endl;
    t1.join();
    return 0;
}

5. 多线程的创建

5.1 普通函数创建线程

#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;

//普通函数充当线程处理函数创建线程
void print() {
    int count = 5;
    while (count--) {
        cout << "普通函数充当线程处理函数" << endl;
        Sleep(1000);
    }
}
void test01() {
    thread t1(print);
    t1.join();
}
int main() {
    test01();

    return 0;
}

5.2 Lambda创建线程

#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;

//使用Lambda表达式充当线程处理函数
void test02() {
    thread t1([]() {
        cout << "Lambda表达式充当线程处理函数" << endl;
    });
    t1.join();
}

int main() {
    test02();

    return 0;
}

5.3 带参函数创建线程

5.3.1 普通类型参数

#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;

//普通参数
void printData(int num) { 
    while (num--) {
        cout << "num: " << num << endl;
        Sleep(1000);
    }
}
void test03() {
    int num = 5;
    thread t1(printData, num);
    t1.join();
}

int main() {
    test03();

    return 0;
}

5.3.2 结构体类型参数

#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;

//结构体参数的传递
struct MM {
    string name;
    int age;
};
void printMMData(MM mm) {
    cout << "name: " << mm.name << "\tage: " << mm.age << endl;
}
void test04() {
    MM mm = { "貂蝉",18 };
    thread t1(printMMData, mm);
    t1.join();
}

int main() {
    test04();

    return 0;
}

5.3.3 引用参数

#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;

//引用参数
void printReference(int& num) {
    num = 5;
    while (num--) {
        cout << "num: " << num << endl;
        Sleep(1000);
    }
}
void test05() {
    int num = 10;
    thread t1(printReference, ref(num)); //包装引用作为传递的值
    t1.join();
    cout << "主线程 num: " << num << endl;
}

int main() {
    test05();

    return 0;
}

5.3.4 智能指针作为参数传递

#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;

//智能指针当做函数参数
void printPtr(unique_ptr<int> ptr) {
    cout << "智能指针方式: " << ptr.get() << endl;
    cout << "智能指针方式: " << *ptr.get() << endl;
}
void test06() {
    unique_ptr<int> ptr(new int(10));
    cout << "主线程1: ptr: " << ptr.get() << endl;
    cout << "主线程1: ptr: " << *ptr.get() << endl;
    thread t1(printPtr, move(ptr));
    t1.join();
    //进行移动之后, 此处地址为0
    cout << "主线程2: ptr: " << ptr.get() << endl;  
}

int main() {
    test06();

    return 0;
}

5.4 类的成员函数创建线程

5.4.1 仿函数形式

#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;

//仿函数形式(使用类名的方式调用)
class Stu {
private:
public:
    void operator()() {
        cout << "重载()的方式实现 " << endl;
    }
protected:
};
void test07() {
    //有名对象
    Stu stu;
    thread t1(stu);
    t1.join();
    //匿名对象
    thread t2((Stu()));
    t2.join();
}

int main() {
    test07();

    return 0;
}

5.4.2 普通成员函数方式

#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;

//通过类中的成员函数创建
class MM2 {
public:
    void print(int& num) {
        num = 5;
        while (num--) {
            cout << "子线程: num: " << num << endl;
            Sleep(1000);
        }
    }
protected:
private:
};

void test08() {
    MM2 mm;
    int num = 10;
    //指定哪一个类的函数    属于哪一个对象
    thread t1(&MM2::print, mm, ref(num)); //不能传入常量,例: ref(10)
    t1.join();
}

int main() {
    test08();

    return 0;
}

6. 与进程相比,线程的优点

  1. 线程启动速度更快,更轻量级
  2. 系统资源开销更少,执行速度更快,比如共享内存这种通信方式比任何其他的通信方式都快
相关文章
|
27天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
3天前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
363 14
|
19天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
6天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
21天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
23天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2592 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
5天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
181 2
|
3天前
|
编译器 C#
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
105 65
|
6天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
331 2
|
23天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1580 17
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码