Chaichai
多线程编程

多线程编程

多线程编程(基于C++)

目录

什么是多线程

首先我们需要了解什么是线程。
  • 线程(Thread)是操作系统能够进行调度的最小单位,它比进程更小,是进程的一个子集。线程是程序执行的最小单位,用于实现并发执行。
  • 多线程指的是一个程序中包含两个或者两个以上的线程,多线程的提出是为提高代码的执行效率,这就好比工厂中的流水线,只有一条称为单线程,有多条流水线就称为多线程。
我这里还有更形象一点的例子。AI生成

  • 线程比喻:餐厅里的服务员

想象你走进一家餐厅,餐厅就像一个程序,而餐厅里的服务员就像线程。

  1. 餐厅(进程)
  • 餐厅是整个程序的运行环境,它有自己的资源,比如厨房(内存)、餐具(文件句柄)、服务员(线程)等。
  • 餐厅的资源是有限的,但它可以同时为多个顾客服务。
  1. 服务员(线程)
  • 服务员是餐厅里的线程,他们负责完成各种任务,比如点菜、送餐、清理桌子等。
  • 每个服务员可以独立完成任务,但他们共享餐厅的资源(比如厨房和餐具)。
  1. 顾客(任务)
  • 餐厅里的顾客代表程序中的任务。每个顾客有不同的需求,比如点不同的菜或者要求不同的服务。
  • 一个服务员可以同时服务多个顾客,这就是线程的并发性。
线程的特点(用餐厅来比喻)

轻量级

  • 服务员的加入和离开(线程的创建和销毁)相对容易,不需要重新装修餐厅(进程的资源)。
  • 一个服务员(线程)的开销比重新开一家餐厅(进程)小得多。
    并发性
  • 多个服务员可以同时为不同的顾客服务,就像多个线程可以同时执行不同的任务。
  • 这样可以提高餐厅的效率,尤其是在顾客很多的时候。
    共享资源
  • 所有的服务员共享餐厅的厨房、餐具等资源,就像线程共享进程的内存和文件句柄。
  • 这使得服务员之间可以高效地协作,比如一个服务员可以去厨房取菜,另一个服务员可以去送菜。
    独立性
  • 每个服务员有自己的任务和职责,比如点菜、送餐等,就像线程有自己的执行栈和局部变量。
  • 他们可以独立完成任务,但也可以协作完成复杂的任务。
    线程的生命周期(用餐厅来比喻)
    新建(New)
  • 一个新服务员被招聘进来,但还没有开始工作。他站在餐厅门口,等待分配任务。
    就绪(Runnable)
  • 服务员已经准备好工作,等待餐厅经理(操作系统)的调度,比如分配一个顾客给他服务。
    运行(Running)
  • 服务员正在为顾客服务,比如点菜、送餐等。
    阻塞(Blocked)
  • 服务员因为某些原因暂时无法继续服务,比如等待厨房准备好菜,或者顾客还没决定点什么菜。
    终止(Terminated)
  • 服务员完成了所有任务,或者被餐厅经理(操作系统)解雇了,不再工作。
总结

线程就像餐厅里的服务员,它们是程序(餐厅)中的独立执行单元,可以并发执行任务,共享资源,但又独立完成自己的工作。通过合理安排线程(服务员),可以大大提高程序(餐厅)的效率。

创建线程

首先需要额外引入这三个库

#include <thread>
#include <chrono>
#include <atomic>

简单解释一下库的作用:
(不能很好的理解的话可以去问问deepseek,它能给出更通俗的解释)

  • thread

    C++用于线程创建的库

  • chrono

    用于处理时间相关的功能,包括时间点、时间间隔、时钟等

  • atomic

    原子操作是指不可分割的操作。在多线程环境中,当多个线程同时访问和修改一个共享变量时,普通的读写操作可能会导致竞态条件(race condition),从而导致数据不一致。而原子操作可以确保在执行过程中不会被其他线程中断,从而保证操作的完整性。

通过类来创建一个线程

class Mythod
{
public:
    Mythod()
    {
        state = 1;
    }
    // 运行线程的函数
    void run()
    {
        while (state)
        {
            this_thread::sleep_for(chrono::milliseconds(500));
            cout << "Hello!" << endl;
        }
    }
    // 停止线程的函数
    void stop()
    {
        state = 0;
    }

private:
    atomic<bool> state;// 原子操作
};

对于主程序

int main()
{
    Mythod mythod;
    thread thread(&Mythod::run, ref(mythod));

    this_thread::sleep_for(chrono::seconds(3));

    mythod.stop(); // 调用停止线程的方法

    thread.join(); // 用于等待线程结束

    cout << "线程任务执行结束" << endl;

    return 0;
}

线程管理

xx.join() // 用于等待线程完成执行。如果不调用 join() 或 detach() 而直接销毁线程对象,会导致程序崩溃。
xx.detach() // 将线程与主线程分离,线程在后台独立运行,主线程不再等待它。但是它必须在主线程结束之前结束,否则也会导致程序崩溃。

线程的传参

无参数

<thread_name>(func);

值传递

<thread_name>(func,arg1,arg2,...);

引用传递

<thread_name>(func,std::ref(arg1),...) // 涉及引用就用std::ref()

线程的同步与互斥

同步与互斥这两个概念主要用于控制多个线程对共享资源(同一个变量)的访问,以避免数据竞争、死锁等问题

互斥量(Mutex)

互斥量是一种同步原语,用于防止多个线程同时访问共享资源。当一个线程需要访问共享资源时,它首先需要锁定(lock)互斥量。如果互斥量已经被其他线程锁定,那么请求锁定的线程将被阻塞,直到互斥量被解锁(unlock)。

std::mutex mtx;
mtx.lock();   // 锁定互斥锁
// 访问共享资源
mtx.unlock(); // 释放互斥锁

std::lock_guardstd::unique_lock:自动管理锁的获取和释放。

std::lock_guard<std::mutex> lock(mtx); // 自动锁定和解锁
// 访问共享资源

这只是我的一点点肤浅的学习,要更加深入了解多线程编程还得去阅读更多的文档以及去多多实践

参考资料

https://www.runoob.com/cplusplus/cpp-multithreading.html
https://blog.csdn.net/yahid/article/details/125921786
https://blog.csdn.net/luoweifu/article/details/46835437

Author:Chaichai
Link:https://chaichai438.github.io/2025/04/28/RM学习/250426/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可