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. 系统资源开销更少,执行速度更快,比如共享内存这种通信方式比任何其他的通信方式都快
相关文章
|
1天前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
15 6
|
1天前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
7 0
C++ 多线程之线程管理函数
|
1天前
|
资源调度 Linux 调度
Linux C/C++之线程基础
这篇文章详细介绍了Linux下C/C++线程的基本概念、创建和管理线程的方法,以及线程同步的各种机制,并通过实例代码展示了线程同步技术的应用。
9 0
Linux C/C++之线程基础
|
3天前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
3天前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
5天前
|
编译器 C语言 C++
C++入门3——类与对象2-2(类的6个默认成员函数)
C++入门3——类与对象2-2(类的6个默认成员函数)
20 3
|
5天前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
15 2
|
6天前
|
安全 C语言 C++
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
27 4
|
4天前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
36 1
|
5天前
|
编译器 C语言 C++
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
11 1