C++高频知识点(十八)

文章目录

  • [86. C++多线程中,锁的实现方式有哪些?](#86. C++多线程中,锁的实现方式有哪些?)
    • [1. 互斥锁(Mutex)](#1. 互斥锁(Mutex))
    • [2. 递归互斥锁(Recursive Mutex)](#2. 递归互斥锁(Recursive Mutex))
    • [3. 读写锁(Shared Mutex)](#3. 读写锁(Shared Mutex))
    • [4. 自旋锁(Spinlock)](#4. 自旋锁(Spinlock))
    • [5. 条件变量(Condition Variable)](#5. 条件变量(Condition Variable))
  • [87. 内存对齐是什么,为什么要做内存对齐](#87. 内存对齐是什么,为什么要做内存对齐)
  • [88. 结构体和联合体的区别?](#88. 结构体和联合体的区别?)
  • [89. struct和class的区别?](#89. struct和class的区别?)
  • [90. 为什么TCP握手是3次,不能是2次和4次吗?](#90. 为什么TCP握手是3次,不能是2次和4次吗?)

86. C++多线程中,锁的实现方式有哪些?

1. 互斥锁(Mutex)

互斥锁(std::mutex)是最常见的同步机制,用于保护临界区,使得同一时刻只有一个线程可以访问共享资源。

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;

void print_safe(const std::string& msg) {
    std::lock_guard<std::mutex> guard(mtx); // 自动加锁和解锁
    std::cout << msg << std::endl;
}

int main() {
    std::thread t1(print_safe, "Hello from thread 1");
    std::thread t2(print_safe, "Hello from thread 2");

    t1.join();
    t2.join();

    return 0;
}
  • std::lock_guard:常用于在作用域范围内自动管理锁,确保即使发生异常,锁也会被正确释放。

2. 递归互斥锁(Recursive Mutex)

递归互斥锁(std::recursive_mutex)允许同一个线程多次获得同一把锁,而不会引起死锁。适用于递归函数或需要多次加锁的场景。

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>

std::recursive_mutex rec_mtx;

void recursive_function(int n) {
    if (n <= 0) return;
    std::lock_guard<std::recursive_mutex> guard(rec_mtx);
    std::cout << "Recursion depth: " << n << std::endl;
    recursive_function(n - 1);
}

int main() {
    std::thread t1(recursive_function, 5);
    t1.join();

    return 0;
}

3. 读写锁(Shared Mutex)

读写锁(std::shared_mutex in C++17)允许多个线程同时读取数据,但在写数据时,只有一个线程可以获得写锁。适用于读操作频繁、写操作较少的场景

cpp 复制代码
#include <iostream>
#include <thread>
#include <shared_mutex>
#include <mutex>

std::shared_mutex rw_mtx;
int shared_data = 0;

void read_data() {
    std::shared_lock<std::shared_mutex> lock(rw_mtx);
    std::cout << "Reading data: " << shared_data << std::endl;
}

void write_data(int value) {
    std::unique_lock<std::shared_mutex> lock(rw_mtx);
    shared_data = value;
    std::cout << "Writing data: " << shared_data << std::endl;
}

int main() {
    std::thread t1(read_data);
    std::thread t2(write_data, 42);

    t1.join();
    t2.join();

    return 0;
}

4. 自旋锁(Spinlock)

自旋锁是一种忙等待的锁实现,线程在等待锁的过程中会不断检查锁的状态,而不是进入休眠。适用于锁等待时间非常短的场景。

cpp 复制代码
#include <atomic>
#include <thread>

class Spinlock {
    std::atomic_flag flag = ATOMIC_FLAG_INIT;

public:
/*
atomic_flag.test_and_set(acquire):把标志设为 true,返回设之前的值。
返回 false → 说明之前没人持锁 → 你拿到锁。
返回 true → 已经有人持锁 → 你就在 while 里原地打转(自旋)等别人放锁。
atomic_flag.clear(release):把标志清为 false,表示释放锁。
*/
    void lock() {
        // 返回true就一直转圈等
        // Acquire(获取):我看见门开了才进去拿资料。
        // ⇒ 我进门这刻(acquire)之后要读的东西,不允许被提前到进门之前去读;进去后能看见别人关门前放好的资料。
        // test_and_set(acquire):确保拿到锁后再读写共享数据,后续读写不会被提前到拿锁之前。
        while (flag.test_and_set(std::memory_order_acquire)) {
            // 自旋等待,什么也不做
        }
    }

    void unlock() {
        // 放锁
        // Release(释放):我把活儿干完、把资料都装进箱子,再把"门"关上发出通知。
        // ⇒ 门关上这刻(release)之前我做的所有修改,都必须先完成,不能被拖到门关之后。
        // clear(release):确保临界区里对共享数据的修改,在清锁之前完成并对外可见。
        flag.clear(std::memory_order_release);
    }
};

Spinlock spinlock;

void critical_section() {
    spinlock.lock();
    // 访问共享资源
    spinlock.unlock();
}

int main() {
    std::thread t1(critical_section);
    std::thread t2(critical_section);

    t1.join();
    t2.join();

    return 0;
}
cpp 复制代码
#include <atomic>
#include <thread>
#include <iostream>
#include <chrono>

// 把下面这个宏改成 1 可以切到 relaxed 版本做对比
// USE_RELAXED=0(默认):ready.store(..., memory_order_release) + ready.load(..., memory_order_acquire)
// ------正确的发布/获取配对。

// USE_RELAXED=1:把两边都改成 memory_order_relaxed
// ------没有跨线程的可见性保证。
#ifndef USE_RELAXED
#define USE_RELAXED 0
#endif

int data = 0;                 // 普通共享数据(非原子)
std::atomic<bool> ready{false}; // 原子标志:告诉对方"数据准备好了"

void producer() {
    data = 42; // 普通写:把共享数据准备好

#if USE_RELAXED
    // 没有发布语义:可能发生重排,消费者即便看到 ready=true,也未必能看到 data=42
    ready.store(true, std::memory_order_relaxed);
#else
    // 发布(release):保证 data 的写先于 ready=true 对外可见
    // 发布:把之前对 data 的写先"封口发布"
    ready.store(true, std::memory_order_release);
#endif
}

int consumer(int& seen) {
#if USE_RELAXED
    while (!ready.load(std::memory_order_relaxed)) {
        // 自旋等待
        std::this_thread::yield();
    }
#else
    // 获取(acquire):保证看到 ready=true 后,再读 data 时能看到发布方的修改
    while (!ready.load(std::memory_order_acquire)) {
        // std::this_thread::yield(); 是 C++11 提供的线程调度提示(在 <thread> 里)。作用是:把当前线程剩余的时间片"让出来",告诉操作系统调度器"我现在没啥可做了,可以先让别的就绪线程跑一会儿"。函数返回 void,不抛异常。
        std::this_thread::yield();          // 让出时间片,避免满负荷空转
    }
#endif

    int x = data;   // 普通读
    seen = x;
    return x == 42 ? 0 : 1; // 返回是否出错
}

int main() {
    // 跑多轮,观察是否有不一致
    const int rounds = 100000;  // 可按机器性能调整
    int failures = 0;

    for (int i = 0; i < rounds; ++i) {
        // 每轮复位
        data = 0;
        ready.store(false, std::memory_order_relaxed);

        int seen = -1;

        std::thread tC([&]{ failures += consumer(seen); });
        std::thread tP(producer);

        tP.join();
        tC.join();

        // 可选:偶尔打印下现场(避免刷屏)
        if (i % 25000 == 0) {
#if USE_RELAXED
            std::cout << "[relaxed] round " << i << ", seen=" << seen << "\n";
#else
            std::cout << "[acq/rel] round " << i << ", seen=" << seen << "\n";
#endif
        }
    }

#if USE_RELAXED
    std::cout << "[relaxed] failures = " << failures
              << " / " << rounds << " (未必容易复现,但理论上可能出错)\n";
#else
    std::cout << "[acquire/release] failures = " << failures
              << " / " << rounds << " (应始终为 0)\n";
#endif

    return 0;
}

5. 条件变量(Condition Variable)

条件变量(std::condition_variable)并不单独作为锁,而是与互斥锁结合使用。它允许线程在等待某个条件满足时被阻塞,并在条件满足时被唤醒。

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void worker() {
    std::unique_lock<std::mutex> lock(mtx);
    // lock_guard 没有 unlock()/lock() 接口,生命周期内"始终持有"是它的契约;而 unique_lock 提供了这些成员函数并允许临时失去所有权
    // lock_guard 的设计是极简 RAII:构造即上锁、析构即解锁,中间不允许变更持锁状态,也不可移动。
    // unique_lock 的设计是"可拥有/可转移/可显式 lock/unlock/release",契合 wait 在等待期间修改持锁状态的需求。
    // 这是 条件变量的"带谓词等待" 用法。
    // cv.wait(lock, [] { return ready; }); 的意思是:在持有 lock(一个 std::unique_lock<std::mutex>)的前提下,一直等到 ready 为 true 才返回;否则就阻塞等待通知。
    cv.wait(lock, [] { return ready; }); // 等待条件变量
    std::cout << "Worker thread is running..." << std::endl;
}

void signal() {
    std::lock_guard<std::mutex> lock(mtx);
    ready = true;
    std::cout << "signal thread is running..." << std::endl;
    cv.notify_one(); // 唤醒等待中的线程
}

int main() {
    std::thread t1(worker);
    std::thread t2(signal);

    t1.join();
    t2.join();

    return 0;
}

87. 内存对齐是什么,为什么要做内存对齐


88. 结构体和联合体的区别?

cpp 复制代码
#include <iostream>
#include <cstring>  // 用于使用 strcpy

union Data {
    int i;
    float f;
    char str[20];
};

int main() {
    Data data;  // 创建一个联合体变量

    // 使用 int 成员
    data.i = 42;
    std::cout << "data.i: " << data.i << std::endl;

    // 使用 float 成员
    data.f = 3.14;
    std::cout << "data.f: " << data.f << std::endl;

    // 使用字符串成员
    strcpy(data.str, "Hello");
    std::cout << "data.str: " << data.str << std::endl;

    // 观察内存重用的效果,可能会出现未定义的 乱七八糟的内容
    std::cout << "After setting data.str, data.i: " << data.i << std::endl;
    std::cout << "After setting data.str, data.f: " << data.f << std::endl;

    return 0;
}


//上面的代码输出 可能输出下面的结果
// data.i: 42 
// data.f: 3.14 
// data.str: Hello After setting data.str, data.i: 1819043144  // 这些值可能是未定义的行为 
// After setting data.str, data.f: 1.15282e+09 // 因为内存已经被str覆盖

89. struct和class的区别?

90. 为什么TCP握手是3次,不能是2次和4次吗?




之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!

相关推荐
Q741_1471 小时前
如何判断一个数是 2 的幂 / 3 的幂 / 4 的幂 / n 的幂 位运算 总结和思考 每日一题 C++的题解与思路
开发语言·c++·算法·leetcode·位运算·总结思考
半瓶啤酒一醉方休1 小时前
C# 查询电脑已安装所有软件并打印txt保存到桌面
开发语言·c#
钢铁男儿2 小时前
深入解析C#并行编程:从并行循环到异步编程模式
开发语言·c#
小杜的生信筆記3 小时前
基于R语言,“上百种机器学习模型”学习教程 | Mime包
开发语言·学习·机器学习·r语言·sci
mit6.8243 小时前
修复C++14兼容性问题& 逻辑检查
开发语言·c++
沐知全栈开发4 小时前
MongoDB 高级索引
开发语言
御承扬4 小时前
HarmonyOS NEXT系列之编译三方C/C++库
c语言·c++·harmonyos
许怀楠4 小时前
【C++】类和对象(下)
c++
测试界清流4 小时前
Postman接口测试入门
开发语言·lua