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. 系统资源开销更少,执行速度更快,比如共享内存这种通信方式比任何其他的通信方式都快
相关文章
|
14天前
|
缓存 安全 C++
C++无锁队列:解锁多线程编程新境界
【10月更文挑战第27天】
29 7
|
14天前
|
消息中间件 存储 安全
|
21天前
|
存储 并行计算 安全
C++多线程应用
【10月更文挑战第29天】C++ 中的多线程应用广泛,常见场景包括并行计算、网络编程中的并发服务器和图形用户界面(GUI)应用。通过多线程可以显著提升计算速度和响应能力。示例代码展示了如何使用 `pthread` 库创建和管理线程。注意事项包括数据同步与互斥、线程间通信和线程安全的类设计,以确保程序的正确性和稳定性。
|
22天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
16 3
|
22天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
15 2
|
22天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
28 2
|
22天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
28 1
|
22天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
33 1
|
6天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
29 4
|
7天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
23 4