尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

C++多线程

C++多线程
📅 发布时间:2026/6/20 2:25:20
  • 虚函数

在C++中, virtual 和 override 关键字用于支持多态,尤其是在涉及类继承和方法重写的情况下。正确地理解和使用这两个关键字对于编写可维护和易于理解的面向对象代码至关重要。

virtual 关键字

  1. 使用场景:在基类中声明虚函数。
  2. 目的:允许派生类重写该函数,实现多态。
  3. 行为:当通过基类的指针或引用调用一个虚函数时,调用的是对象实际类型的函数版本
  4. 示例:
class Base {
public:virtual void func() {std::cout << "Function in Base" << std::endl;}
};

override 关键字

  1. 使用场景:在派生类中重写虚函数。
  2. 目的:明确指示函数意图重写基类的虚函数。
  3. 行为:确保派生类的函数确实重写了基类中的一个虚函数。如果没有匹配的虚函数,编译器会报错。
  1. 示例:
class Derived : public Base {
public:void func() override {std::cout << "Function in Derived" << std::endl;}
};

注意点

只在派生类中使用 override: override 应仅用于派生类中重写基类的虚函数。

虚析构函数:如果类中有虚函数,通常应该将析构函数也声明为虚的。

默认情况下,成员函数不是虚的:在C++中,成员函数默认不是虚函数。只有显式地使用 virtual关键字才会成为虚函数。

继承中的虚函数:一旦在基类中声明为虚函数,该函数在所有派生类中自动成为虚函数,无论是否使用 virtual 关键字。

正确使用 virtual 和 override 关键字有助于清晰地表达程序员的意图,并利用编译器检查来避免常见的错误,如签名不匹配导致的非预期的函数重写。


  • 抽象类

抽象类的特点

  1. 包含至少一个纯虚函数:
  • 抽象类至少有一个纯虚函数。这是一种特殊的虚函数,在抽象类中没有具体实现,而是留给派生类去实现。
  • 纯虚函数的声明方式是在函数声明的末尾加上 = 0 。
  1. 不能直接实例化:
  • 由于抽象类不完整,所以不能直接创建它的对象。就像你不能直接使用“交通工具”的概念去任何地方,你需要一个具体的交通工具。
  1. 用于提供基础结构:
  • 抽象类的主要目的是为派生类提供一个共同的基础结构,确保所有派生类都有一致的接口和行为。

  • 纯虚函数-接口

一个类作为接口可以通过以下步骤来实现:

  1. 定义抽象类:创建一个包含纯虚函数的抽象类,这些函数构成了接口的一部分。这些函数在抽象类

中只有声明而没有具体的实现。

  1. 派生类实现接口:派生类继承抽象类,并实现其中的纯虚函数,以具体实现接口定义的方法。
class LiveMove{
public:virtual void eat() = 0;virtual void bite() = 0;virtual void drink() = 0;virtual void la() = 0;
};class Dog : public LiveMove{
public:void eat() override{...};void bite() override{...};void drink() override{...};void la() override{...};
};

  • 线程的创建

  • 通过lambda表达式创建线程

    Lambda 格式:[捕获列表] (参数) 可选修饰 -> 返回类型 { 函数体 }  

    • 多线程场景中,最常用简化格式:[&](){ 核心逻辑 }(引用捕获所有,无参无返回);
    • 捕获列表是关键:想修改外部变量用 [&],想只读用 [=],想精准控制用 [&var, num];
    • 线程安全:引用捕获要确保变量生命周期,值捕获要注意拷贝成本。

创建一个lambda表达式,定义变量i,将123传入lambda表达式,输出lambda表示式的内容

int main(int argc, char const *argv[]){std::thread th([](int i){std::cout << "test lambda" << i << std::endl;}, 123);th.join();    return 0;
}

 

在类成员函数里定义一个线程,访问类成员变量

class TestLambda{
public:void start(){std::string hr;std::thread th([this](){std::cout << this->name << std::endl;});th.join();}private:std::string name = "hello";
};int main(int argc, char const *argv[]){   TestLambda test;test.start();return 0;
}

  •  竞争状态、临界区、互斥锁

  1. 临界区:一段对「共享资源(如类成员变量、全局变量、堆内存)」进行读写操作的代码。核心特点:同一时间只能有一个线程执行这段代码,否则会出问题。
  2. 竞争状态:多个线程「同时进入临界区」,对共享资源进行读写操作,导致程序行为不可预测(结果不确定、数据损坏、崩溃)的现象。

示例:

创建了100个线程,并调到后台运行,产生竞争状态,导致运行结果不可预测

void TestThread(){//std::this_thread::sleep_for(std::chrono::milliseconds(10));std::cout << "====================" << std::endl;std::cout << "test 001" << std::endl;std::cout << "test 002" << std::endl;std::cout << "test 003" << std::endl;std::cout << "====================" << std::endl;
}int main(int argc, char const *argv[]){//创建100个进程并调至后台运行,实现并行for(int i = 0; i < 100; ++i){std::thread th(TestThread);th.detach();}return 0;
}

为了解决竞争状态,设置互斥锁,建立临界区

通过定义锁,我们定义获取锁资源到释放锁资源的区间叫做临界区,同一时间只能有一个进程访问临界区资源,只有当解锁后,其他被阻塞的线程才能重新获取重复上述操作

static std::mutex mtx;void TestThread(){//获取锁资源,如果获取不到进去阻塞状态mtx.lock();std::cout << "====================" << std::endl;std::cout << "test 001" << std::endl;std::cout << "test 002" << std::endl;std::cout << "test 003" << std::endl;std::cout << "====================" << std::endl;mtx.unlock();
}int main(int argc, char const *argv[]){for(int i = 0; i < 100; ++i){std::thread th(TestThread);th.detach();}return 0;
}

 

  • 常用的mutex函数

lock() 阻塞式获取锁:若锁未被占用则成功获取;若已被占用,线程阻塞直到锁释放。 必须获取锁才能执行临界区(等待可接受)
unlock() 手动释放锁:必须在 lock()/try_lock() 成功后调用,否则行为未定义(崩溃)。 手动管理锁时,临界区结束后释放锁
try_lock() 非阻塞式获取锁:锁未被占用则返回 true(成功);已被占用则返回 false(不阻塞)。 不想阻塞,可先做其他任务(如重试、跳过)
try_lock_for() 超时非阻塞获取:在 rel_time 时间内尝试获取锁,超时未获取则返回 false。 允许短暂等待,但不想无限阻塞(如等待 1 秒)
try_lock_until() 绝对时间超时获取:尝试获取锁直到 abs_time 时间点,超时返回 false。 需精确控制等待截止时间(如凌晨 3 点前)
  • 处理线程抢不到资源问题

我们总是会遇到,当一个线程解锁后,该进程又一次进入了临界区,使得原本等待解锁的被阻塞线程又得接着等待,使得处理效率变低。

通过每次线程解锁时让其线程睡眠,保证解锁后不会与其他被阻塞线程争夺。

示例:

void ThreadMainMtx(int i){for(;;){mtx.lock();std::cout << i << "[in]\n";std::this_thread::sleep_for(std::chrono::milliseconds(100));mtx.unlock();std::this_thread::sleep_for(std::chrono::seconds(1));//解锁后,睡眠1s,再取争锁}
}int main(int argc, char const *argv[]){for(int i = 0; i < 4; ++i){std::thread th(ThreadMainMtx, i + 1);th.detach();}return 0;
}

 

相关新闻

  • 洛谷P2860 [USACO06JAN] Redundant Paths G 题解 边双连通分量
  • AI真好玩系列-免费解锁 Google Gemini 的几种方式
  • 2025权威聚焦:智能门窗控制器解决方案商综合推荐,引领智慧生活新入口 - 星报

最新新闻

  • 如何快速掌握跨设备控制:终极多平台键鼠共享方案
  • 2026年台州市PMP培训机构哪家好?官方授权R.E.P.报考指南 - 众智商学院课程中心
  • 2026衢州2026正规漏水检测维修公司精选口碑榜TOP5权威推荐-精准定位检测漏水点-专业防水补漏堵漏维修、卫生间/厨房/屋顶/天沟/地下室/阳台防水漏水检测维修 - 安佳防水
  • 武汉南华光电职业技术学校 2026 年报名入口以及招生办联系方式 - 武汉中职最新信息发布
  • 4.1 自由振动 固有频率与固有振型
  • ComfyUI TTP Toolset:3步掌握8K超分辨率图像分块处理技术,普通电脑也能轻松实现AI图像增强

日新闻

  • 信任的进化:技术实现详解——如何用JavaScript构建博弈论模拟器
  • Terrakube自定义工作流:如何集成OPA、Infracost等工具扩展IaC能力
  • grunt-concurrent快速入门:5分钟学会并行运行Grunt任务

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号