C++线程同步的几种方式

 更新时间:2026年08月24日 08:42:52   作者:布莱克605  
本文主要介绍了本文系统介绍了多线程编程中常用的同步机制及其适用场景,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1. 引言

在多线程编程中,当多个线程需要访问和修改共享资源时,如果不加以控制,可能会导致数据不一致、竞态条件等问题。线程同步是确保多个线程在访问共享资源时能够正确、有序执行的关键技术。本文将系统性地介绍几种主流的线程同步方式及其适用场景。

2. 线程同步的核心方式

2.1 互斥锁 (Mutex)

互斥锁是最基本、最常用的同步机制。它保证在同一时刻只有一个线程可以进入临界区(访问共享资源的代码段)。

  • 原理:线程在进入临界区前尝试获取锁,若锁已被占用则阻塞等待;离开临界区时释放锁。
  • 特点:简单、通用,但可能引发死锁(如多个锁的获取顺序不当)。
// C++ std::mutex 实现互斥
#include <iostream>
#include <thread>
#include <mutex>

class Counter {
private:
    int count = 0;
    std::mutex mtx;
public:
    void increment() {
        std::lock_guard<std::mutex> lock(mtx);
        count++;
    }
    int getCount() const { return count; }
};

2.2 信号量 (Semaphore)

信号量是一种计数器,用于控制同时访问特定资源的线程数量,允许多个线程(数量由信号量值决定)同时进入临界区。

  • 原理:初始化一个许可数量。线程执行 acquire() 获取许可(数量减1),执行完毕调用 release() 归还许可(数量加1)。当许可数为0时,后续线程将阻塞。
  • 适用场景:资源池管理(如数据库连接池)、流量控制。
// C++ 信号量示例 (C++20 引入 std::counting_semaphore)
#include <iostream>
#include <thread>
#include <semaphore>

std::counting_semaphore<10> semaphore(3); // 允许3个线程同时访问

void accessResource() {
    semaphore.acquire();
    try {
        // 访问共享资源
        std::cout << "Thread accessing resource" << std::endl;
    } catch (...) {
        semaphore.release();
        throw;
    }
    semaphore.release();
}

2.3 条件变量 (Condition Variable)

条件变量通常与互斥锁结合使用,允许线程在某个条件不满足时主动释放锁并进入等待状态,直到其他线程改变条件后将其唤醒。

  • 原理:线程在持有锁的情况下检查条件,若条件不满足则调用 await() 进入等待并释放锁;其他线程改变条件后调用 signal() 或 signalAll() 唤醒等待线程。
  • 适用场景:生产者-消费者模型、线程间协作。
// C++ 使用 std::mutex 和 std::condition_variable
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

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

void workerThread() {
    std::unique_lock<std::mutex> lock(mtx);
    while (!conditionMet) {
        cv.wait(lock); // 释放锁并等待
    }
    // 条件满足,执行操作
    std::cout << "Condition met, proceeding..." << std::endl;
}

2.4 读写锁 (ReadWriteLock)

读写锁将访问分为读操作和写操作,允许多个读线程同时访问共享资源,但写线程必须独占访问。

  • 原理:读锁是共享的,写锁是排他的。当有线程持有写锁时,其他所有读写线程都被阻塞;当没有写锁时,多个读线程可以同时获取读锁。
  • 适用场景:读多写少的场景,如缓存、配置信息。
// C++ std::shared_mutex 示例 (C++17)
#include <iostream>
#include <thread>
#include <shared_mutex>

std::shared_mutex rwLock;
int sharedData = 0;

void reader() {
    std::shared_lock<std::shared_mutex> lock(rwLock);
    // 读取数据
    std::cout << "Reading data: " << sharedData << std::endl;
}

void writer() {
    std::unique_lock<std::shared_mutex> lock(rwLock);
    // 修改数据
    sharedData++;
    std::cout << "Writing data: " << sharedData << std::endl;
}

2.5 屏障 (Barrier) 与 闭锁 (Latch)

闭锁 (CountDownLatch):允许一个或多个线程等待其他线程完成一组操作。计数器初始化后递减,减到0时等待的线程被释放。

屏障 (CyclicBarrier):让一组线程互相等待,直到所有线程都到达某个屏障点后才继续执行。可重复使用。

// C++ std::latch 示例 (C++20)
#include <iostream>
#include <thread>
#include <latch>

std::latch latch(3); // 需要3个线程完成

void worker() {
    // 执行任务
    std::cout << "Worker thread completing task" << std::endl;
    latch.count_down(); // 计数器减1
}

int main() {
    std::thread t1(worker);
    std::thread t2(worker);
    std::thread t3(worker);
    
    latch.wait(); // 主线程等待,直到计数器为0
    
    t1.join();
    t2.join();
    t3.join();
    return 0;
}

3. 选择同步方式的考量因素

  • 性能开销:锁的粒度(粗粒度锁 vs 细粒度锁)、锁竞争程度。
  • 死锁风险:避免循环等待、按固定顺序获取锁、使用带超时的锁。
  • 业务场景:读多写少用读写锁;线程协作用条件变量;控制并发数用信号量。
  • 编程复杂度:synchronized 简单但不够灵活;Lock 接口提供更多控制(如可中断、超时、公平性)。

4. 总结

线程同步是并发编程的基石。理解并正确使用互斥锁、信号量、条件变量、读写锁、屏障/闭锁等机制,可以帮助开发者构建出既高效又安全的多线程程序。在实际开发中,应根据具体场景选择最合适的同步工具,并时刻警惕死锁、活锁、饥饿等并发问题。

到此这篇关于C++线程同步的j几种方式的文章就介绍到这了,更多相关C++线程同步内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++中CSimpleList的实现与测试实例

    C++中CSimpleList的实现与测试实例

    这篇文章主要介绍了C++中CSimpleList的实现与测试实例,较为详细的讲述了C++列表类的实现方法,需要的朋友可以参考下
    2014-10-10
  • C语言实现十六进制与二进制的相互转换

    C语言实现十六进制与二进制的相互转换

    这篇文章主要为大家详细介绍了如何利用c语言实现将文件中十六进制数据与二进制数据相互转换,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的可以学习一下
    2022-11-11
  • C++中关于=default和=delete问题

    C++中关于=default和=delete问题

    这篇文章主要介绍了C++中关于=default和=delete问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • 基于C++中常见内存错误的总结

    基于C++中常见内存错误的总结

    本篇文章是对C++中常见的内存错误进行了总结介绍。需要的朋友参考下
    2013-05-05
  • MYSQL访问之如何使用C/C++连接数据库

    MYSQL访问之如何使用C/C++连接数据库

    本文详细介绍了使用C/C++访问MySQL数据库的方法,包括初始化连接、执行SQL语句和获取查询结果等内容,并强调了连接池的使用以提高性能和资源利用率,感兴趣的朋友一起看看吧
    2026-05-05
  • C++ vector使用的一些注意事项

    C++ vector使用的一些注意事项

    这篇文章主要给大家介绍了关于C++ vector使用的一些注意事项,文中通过示例代码介绍的非常详细,对大家学习或者使用C++具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-08-08
  • C++线程同步实例分析

    C++线程同步实例分析

    这篇文章主要介绍了C++线程同步实例分析,以实例的形式较为深入的分析了C++的线程同步问题,是一个较为经典的线程同步问题,需要的朋友可以参考下
    2014-10-10
  • 单词小助手C语言版

    单词小助手C语言版

    这篇文章主要为大家详细介绍了C语言版的单词小助手,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • C++静态变量,常量的存储位置你真的了解吗

    C++静态变量,常量的存储位置你真的了解吗

    这篇文章主要介绍了C++中静态变量与常量的存储位置的相关资料,需要的朋友可以参考下,希望能够给你带来帮助
    2021-08-08
  • Qt无边框窗口拖拽和阴影的实现方法

    Qt无边框窗口拖拽和阴影的实现方法

    这篇文章主要给大家介绍了关于Qt无边框窗口拖拽和阴影的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11

最新评论