八股C++(二)

八股(一)https://blog.csdn.net/2401_82607598/article/details/159960625?fromshare=blogdetail&sharetype=blogdetail&sharerId=159960625&sharerefer=PC&sharesource=2401_82607598&sharefrom=from_link


https://blog.csdn.net/2401_82607598/article/details/160991619?fromshare=blogdetail&sharetype=blogdetail&sharerId=160991619&sharerefer=PC&sharesource=2401_82607598&sharefrom=from_link

对STL容器的理解

主要分成

  • 顺序容器
  • 关联容器,又分无序容器和有序容器
  • 容器适配器
  • 其他容器

看这个图


Linux常用命令

基本指令

  • pwd,显示当前目录路径
  • ls,查看文件和目录
  • cd,切换目录
  • mkdir,创建目录
  • rmdir,删除空目录
  • touch,创建空文件
  • rm,删除文件或目录
  • mv,重命名或移动文件/目录
  • cp,复制文件或目录

文件查看命令

  • cat,查看文件内容
  • head,从文件头开始查看内容
  • tail,从文件尾部开始查看内容

重定向指令

  • >,覆盖写入

  • >>,追加写入
    时间日期指令,date,cal
    用户管理和组管理,groupadd,groupdel

  • useradd,新增用户

  • userdel,删除用户

  • id,查看用户信息

  • su,切换用户

  • whoami,查看当前用户
    文件权限

  • chmod,更改文件权限

  • chown,改变文件或目录的拥有者

  • umask,查看和修改新建时默认文件权限

网络管理命令

  • ifconfig,查看网卡信息
  • ping,测试与某台主机的连通性
  • curl,用于发送HTTP请求
  • host,查询ip相关的域名
  • netstat,监测网络连接状态

进程相关命令

  • ps,查看进程相关信息,常用选项。。。ps -aux
  • kill、killall,停止杀死进程
  • top,实时显示进程的信息
  • pstree,以树的形式查看进程信息

压缩和解压缩

  • zip,压缩文件或目录

  • unzip,解压

  • gzip,压缩单个文件

  • gunzip,解压gzip压缩的文件

  • tar,打包命令
    搜索查找

  • find,从指定目录下递归遍历各个子目录

  • locate,定位整个系统的文件路径

  • grep,文本搜索

  • which,

  • whereis
    软件包管理

  • yum list

  • yum install

  • yum remove

还有一些零碎的,vim编辑,man查看文档,help,shutdown关机

被问到Linux查看CPU、GPU

  • lscpu,提供了CPU架构的详尽信息,包括类型、核心数、每个核心的线程数、缓存大小
  • cat /proc/cpuinfo,查看/proc/cpuinfo文件,Linux系统中这个文件详细了解到每个CPU核心的参数
  • dmidecode,提供硬件系统的详尽详细
  • lspci,查询所有PCI设备的标准命令,配合grep可以过滤出显卡相关的信息lspci|grep -i vga
  • glxinfo,获取OpenGL图形系统的详细信息,包括GPU的制造商、型号

https://blog.csdn.net/juluwangriyue/article/details/131957682?fromshare=blogdetail&sharetype=blogdetail&sharerId=131957682&sharerefer=PC&sharesource=2401_82607598&sharefrom=from_link


互斥锁和读写锁的区别

  • 互斥锁 :互斥锁加锁失败后,线程会释放CPU,给其他线程
  • 自旋锁加锁失败后,线程会忙等待,直到它拿到锁
  • 读写锁由读锁和写锁两部分组成 ,如果只读取共享资源用读锁加锁,如果要修改共享资源用写锁加锁。读写锁适用于能明确区分读操作和写操作的场景。

前面提到的互斥锁、自旋锁、读写锁,都是属于悲观锁。悲观锁做事比较悲观,它认为多线程同时修改共享资源的概率比较高,于是很容易出现冲突,所以访问共享资源前,先要上锁。那相反的,如果多线程同时修改共享资源的概率比较低,就可以采用乐观锁。


多数据的数据共享

多线程数据共享核心原则:保证线程安全,控制可见性,有序性,原子性

  • 无锁方案(原子变量atomic、线程局部存储TLS)
  • 互斥同步(主流方案mutex互斥锁、读写锁,最常用)搭配RAll智能锁lock_guard、unique_lock
  • 消息队列/生产者消费者(解耦共享,规避临界区)
  • 设计层规避(尽量少共享数据)

高并发内存池。线程局部存储(threadlocalstorage,简称TLS)是一种存储机制,它允许每个线程拥有自己的变量副本。每个线程可以无锁的获取自己的ThreadCache对象。

C++11之后,可以直接用C++ 关键字thread_local去做声明,就可以定义线程局部存储变量了,这样可以跨平台。

线程局部存储 TLS线程独有数据

数据名义上全局,实际每个线程一份副本,线程间完全不共享,天然安全。

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

thread_local int g_num = 0; // 每个线程独立副本

void func(int id) {
    g_num = id;
    cout << "线程" << id << " num = " << g_num << endl;
}

int main() {
    thread t1(func, 1);
    thread t2(func, 2);
    t1.join();
    t2.join();
    return 0;
}

线程局部存储,配合单例模式和智能指针。。。
结合C++多线程场景,分核心方案、选型、代码示例、避坑要点,按面试/工程实用角度整理,聚焦多线程共享数据全套解法。

一、总体思路

多线程共享数据核心原则:保证线程安全 + 控制可见性、有序性、原子性

主要分四大类方案:

  1. 无锁方案(原子变量、线程局部存储)

  2. 互斥同步(互斥锁、读写锁,最常用)

  3. 消息队列/生产者消费者(解耦共享,规避临界区)

  4. 设计层面规避(尽量少共享数据)

二、方案详解 + 适用场景 + 代码

1. 优先方案:减少共享(最佳实践)

能不共享就不共享,从根源解决线程安全问题。

• 每个线程使用私有局部变量,不对外暴露

• 任务拆分,数据随任务走,而非全局共享

• 函数传参尽量值传递/右值引用,少用裸指针/全局引用

面试话术:多线程设计第一准则:数据私有化,弱化共享。
2. 线程局部存储 TLS:线程独有数据

数据名义上全局,实际每个线程一份副本,线程间完全不共享,天然安全。

C++ 关键字:thread_local

示例

cpp 复制代码
#include <iostream>
#include <thread>
using namespace std;

thread_local int g_num = 0; // 每个线程独立副本

void func(int id) {
    g_num = id;
    cout << "线程" << id << " num = " << g_num << endl;
}

int main() {
    thread t1(func, 1);
    thread t2(func, 2);
    t1.join();
    t2.join();
    return 0;
}

适用场景

• 每个线程需要独立的全局状态、计数器、缓存

• 完全不需要线程间数据交互

特点

• 无线程竞争、无锁、性能极高

• 数据不互通,无法用来做多线程数据交互
3. 轻量级:原子变量 std::atomic(简单数值共享)

针对单个整型、指针、布尔值共享,无锁,依靠CPU原子指令实现线程安全。

无需加锁,开销远低于互斥锁。

常用类型

std::atomic<int> / std::atomic<bool> / std::atomic<long> 等

示例(多线程计数)

cpp 复制代码
#include <iostream>
#include <thread>
#include <atomic>
using namespace std;

atomic<int> g_cnt = 0; // 原子变量,线程安全

void add() {
    for (int i = 0; i < 10000; ++i) {
        g_cnt++; // 自增原子操作
    }
}

int main() {
    thread t1(add);
    thread t2(add);
    t1.join();
    t2.join();
    cout << "总数:" << g_cnt << endl; // 结果正确 20000
    return 0;
}

适用 & 限制

✅ 适用:计数器、状态标志、开关、简单数值同步

❌ 不适用:复合操作、结构体、对象、多条语句(如 if + 赋值 组合依然有竞态)

补充:内存序

默认 std::memory_order_seq_cst(顺序一致),追求极致性能可放宽内存序(面试了解即可)。
4. 主流方案:互斥锁 mutex(复杂共享数据)

多个线程读写对象、容器、多变量组合,使用互斥锁保护临界区,同一时刻仅一个线程访问。

搭配 RAII 智能锁 std::lock_guard / std::unique_lock,避免漏解锁。

基础示例(共享容器)

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
using namespace std;

vector<int> g_vec;
mutex g_mtx;

void write() {
    for (int i = 0; i < 100; ++i) {
        // RAII 上锁,出作用域自动解锁
        lock_guard<mutex> lock(g_mtx);
        g_vec.push_back(i);
    }
}

void read() {
    while (true) {
        lock_guard<mutex> lock(g_mtx);
        if (!g_vec.empty()) {
            cout << g_vec.back() << " ";
            break;
        }
    }
}

int main() {
    thread t1(write);
    thread t2(read);
    t1.join();
    t2.join();
    return 0;
}

关键要点

  1. 临界区尽量短小:只把读写共享数据的代码加锁,无关逻辑移出

  2. 禁止锁内耗时操作(IO、sleep、网络),严重阻塞并发

  3. 避免死锁:多锁保持固定加锁顺序、不嵌套锁

  4. 读多写少专用:读写锁 shared_mutex

承接上一轮问答,读共享、写独占,高并发读场景大幅提升性能。

示例

cpp 复制代码
#include <iostream>
#include <thread>
#include <shared_mutex>
#include <string>
using namespace std;

string g_config = "default";
shared_mutex g_rw_mtx;

// 读线程:共享锁(多线程同时读)
void read_config(int id) {
    shared_lock<shared_mutex> lock(g_rw_mtx);
    cout << "读线程" << id << ": " << g_config << endl;
}

// 写线程:独占锁(排他所有读写)
void write_config() {
    unique_lock<shared_mutex> lock(g_rw_mtx);
    g_config = "new setting";
    cout << "写线程更新配置" << endl;
}

int main() {
    thread t1(read_config, 1);
    thread t2(read_config, 2);
    thread t3(write_config);
    t1.join(); t2.join(); t3.join();
    return 0;
}

适用场景

• 配置数据、全局缓存、静态报表、查询远多于修改
6. 解耦方案:生产者/消费者 + 阻塞队列

不直接共享全局数据,用线程安全队列中转数据,线程间通过队列通信,彻底隔离临界区。

工程中高频用于:日志、任务队列、数据转发。

简易线程安全队列

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <queue>
#include <condition_variable>
using namespace std;

template<typename T>
class SafeQueue {
private:
    queue<T> q;
    mutable mutex mtx;
    condition_variable cv;
public:
    void push(T val) {
        lock_guard<mutex> lock(mtx);
        q.push(val);
        cv.notify_one(); // 唤醒等待线程
    }

    bool pop(T& val) {
        unique_lock<mutex> lock(mtx);
        // 队空则等待
        cv.wait(lock, [this](){ return !q.empty(); });
        val = q.front();
        q.pop();
        return true;
    }
};

SafeQueue<int> data_queue;

// 生产者
void producer() {
    for (int i = 1; i <= 5; ++i) {
        data_queue.push(i);
    }
}

// 消费者
void consumer() {
    int val;
    while (data_queue.pop(val)) {
        cout << "消费:" << val << endl;
    }
}

int main() {
    thread t1(producer);
    thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}

优势

• 线程解耦,读写分离,逻辑清晰

• 配合条件变量,实现阻塞等待,无忙等浪费CPU

三、方案选型速查表(面试直接背)

场景 推荐方案 优点

完全不需要数据互通、每个线程独立数据 thread_local 无锁、性能最高

单个数值/布尔、简单计数/状态 std::atomic 无锁、轻量

普通读写、数据结构/对象共享、读写均衡 std::mutex 互斥锁 简单通用、不易出错

读多写少(配置、缓存) std::shared_mutex 读写锁 高并发读性能好

线程间数据流转、任务分发、日志 阻塞队列(队列+互斥锁+条件变量) 解耦、架构清晰

四、通用避坑要点(面试高频考点)

  1. 竞态条件:多条语句操作共享变量,必须加锁,原子变量只保护单条原子操作。

  2. 临界区最小化:锁范围越小,并发越高。

  3. 死锁防范:多锁顺序一致、不嵌套锁、不在锁内调用外部函数。

  4. 可见性问题:多线程共享变量,不要依赖编译器优化,优先用锁/原子保证内存可见。

  5. 拒绝裸全局变量:尽量封装成类,把锁和数据绑定在一起(封装线程安全类)。

  6. 锁粒度选择:粗粒度锁(一把锁保护所有数据)简单但并发低;细粒度锁(多分锁)并发高但易死锁。

五、面试一句话总结

  1. 能不共享就不共享,优先 thread_local 私有化数据;

  2. 简单数值用原子变量无锁实现;

  3. 常规对象/容器共享用互斥锁;

  4. 读多写少场景改用读写锁提升并发;

  5. 线程间数据流转,用阻塞队列解耦,是工程最优实践。


C++新标准新特性(后面补充

  • auto
  • nullptr
  • for语法糖
  • 列表初始化
  • override
  • delete
  • default关键字
  • 匿名函数
  • 右值引用
  • 线程类
  • STL标准模板库

左值和右值,通俗话讲了。。move语句

左值右值都是表示数据的表达式。

左值具有地址,可以出现在赋值号左侧,可以取地址变量、对象、数组元素都是左值。。

右值通常没有地址 存储在寄存器或临时内存中,不能出现在赋值号的左侧不能取地址 (除非是绑定到const左值引用中),字面量、表达式计算结果都是右值。

  • 左值,表示一个占据内存中可识别位置的一个对象,如变量名或解引用的指针,我们可以获取它的地址,可以给他赋值。
  • 右值,如字面常量、表达式返回值、函数返回值。不能对变量或表达式取地址

左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。右值可以出现在赋值符号的右边,但不能在左边,右值不能取地址。

move用于将一个对象的资源移动到另一个对象,而不是拷贝资源。


指针和智能指针,指针是什么

git分支相关

三次握手和四次挥手(总结成通俗的话

python里面装饰器

相关推荐
影寂ldy1 小时前
C#数组的高级方法
开发语言·c#
zzzsde1 小时前
【Linux网络】传输层协议UDP
linux·服务器·开发语言·网络·算法·udp
格发许可优化管理系统2 小时前
解决Mentor许可冲突,让您的业务无缝运行
运维·服务器·c语言·c++·人工智能
曹牧2 小时前
C#:基类中定义泛型方法
java·开发语言·c#
游乐码2 小时前
c#基础(七)延迟函数
开发语言·unity·c#·游戏引擎
思麟呀2 小时前
在C++基础上理解CSharp-4
开发语言·jvm·c++·c#
Brilliantwxx2 小时前
【算法题】 面试级别的二叉树题目OJ复习(上)
数据结构·c++·笔记·算法·面试
颖火虫盟主2 小时前
Conan C++ 包管理工具深度解析
java·jvm·c++