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. 系统资源开销更少,执行速度更快,比如共享内存这种通信方式比任何其他的通信方式都快
相关文章
|
4月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
196 0
|
4月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
5月前
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
357 5
|
9月前
|
Python
python3多线程中使用线程睡眠
本文详细介绍了Python3多线程编程中使用线程睡眠的基本方法和应用场景。通过 `time.sleep()`函数,可以使线程暂停执行一段指定的时间,从而控制线程的执行节奏。通过实际示例演示了如何在多线程中使用线程睡眠来实现计数器和下载器功能。希望本文能帮助您更好地理解和应用Python多线程编程,提高程序的并发能力和执行效率。
343 20
|
9月前
|
安全 Java C#
Unity多线程使用(线程池)
在C#中使用线程池需引用`System.Threading`。创建单个线程时,务必在Unity程序停止前关闭线程(如使用`Thread.Abort()`),否则可能导致崩溃。示例代码展示了如何创建和管理线程,确保在线程中执行任务并在主线程中处理结果。完整代码包括线程池队列、主线程检查及线程安全的操作队列管理,确保多线程操作的稳定性和安全性。
|
11月前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
204 1
|
缓存 安全 C++
C++无锁队列:解锁多线程编程新境界
【10月更文挑战第27天】
815 7
|
消息中间件 存储 安全
|
数据采集 Java Python
爬取小说资源的Python实践:从单线程到多线程的效率飞跃
本文介绍了一种使用Python从笔趣阁网站爬取小说内容的方法,并通过引入多线程技术大幅提高了下载效率。文章首先概述了环境准备,包括所需安装的库,然后详细描述了爬虫程序的设计与实现过程,包括发送HTTP请求、解析HTML文档、提取章节链接及多线程下载等步骤。最后,强调了性能优化的重要性,并提醒读者遵守相关法律法规。
363 0
|
9月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。

热门文章

最新文章