当前位置: 首页
编程语言
Linux C++程序如何实现并发控制

Linux C++程序如何实现并发控制

热心网友 时间:2026-05-03
转载

在Linux环境下用C++搞并发,方法其实挺多的。选哪种,关键得看你的具体场景。下面咱们就聊聊几种常见的并发控制机制,并配上可以直接跑起来的代码示例。

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

Linux C++程序如何实现并发控制

1. 使用互斥锁(Mutex)

互斥锁,可以说是并发编程里的“老大哥”了。它的任务很明确:保护共享资源,确保同一时间只有一个线程能碰它。这能有效防止数据竞争,但用不好也容易导致死锁。

#include 
#include 
#include 

std::mutex mtx; // 全局互斥锁
int shared_data = 0;

void increment() {
    mtx.lock();   // 加锁
    ++shared_data;
    mtx.unlock(); // 解锁
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    t1.join();
    t2.join();
    std::cout << "Shared data: " << shared_data << std::endl;
    return 0;
}

看这段代码,两个线程t1t2都想增加shared_data。没有锁的话,结果可能出乎意料;有了mtx这把锁,就能保证每次加法操作都是原子的。不过要提醒一点,手动调用lock()unlock()需要格外小心,万一在锁中间抛个异常或者忘了解锁,就麻烦了。所以更常见的做法是使用std::lock_guard

2. 使用递归互斥锁(Recursive Mutex)

普通互斥锁有个特点:同一个线程重复上锁会导致死锁。那如果函数会递归调用自己,或者需要多层加锁呢?这时候递归互斥锁就派上用场了。它允许同一个线程多次获得锁,只要解锁次数匹配就行。

#include 
#include 
#include 

std::recursive_mutex mtx; // 全局递归互斥锁
int shared_data = 0;

void increment(int count) {
    if (count <= 0) return;
    mtx.lock();   // 加锁
    ++shared_data;
    std::cout << "Thread " << std::this_thread::get_id() << " incremented shared_data to " << shared_data << std::endl;
    mtx.unlock(); // 解锁
    increment(count - 1); // 递归调用
}

int main() {
    std::thread t1(increment, 5);
    std::thread t2(increment, 3);
    t1.join();
    t2.join();
    std::cout << "Final shared data: " << shared_data << std::endl;
    return 0;
}

这个例子里,increment函数会递归调用自己。如果用普通mutex,线程第一次进入函数锁上后,在递归调用时试图再次获取锁,就会把自己永远挂起。换成recursive_mutex就顺畅了。当然,递归锁通常比普通锁开销大一点,非必要不使用。

3. 使用条件变量(Condition Variable)

条件变量解决的是另一类问题:线程间的协同等待。比如,一个线程需要等待某个条件成立(比如数据准备好了)才能继续,而另一个线程负责在条件满足时通知它。这是实现生产者-消费者模型的核心工具之一。

#include 
#include 
#include 
#include 

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

void worker() {
    std::unique_lock lock(mtx);
    cv.wait(lock, []{ return ready; }); // 等待条件成立
    std::cout << "Worker thread is processing data..." << std::endl;
}

void trigger() {
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟一些准备工作
    {
        std::lock_guard lock(mtx);
        ready = true;
    }
    cv.notify_one(); // 通知一个等待的线程
}

int main() {
    std::thread t1(worker);
    std::thread t2(worker);
    trigger();
    t1.join();
    t2.join();
    return 0;
}

注意看cv.wait的用法,它接受一个锁和一个谓词(lambda表达式)。这样写可以防止“虚假唤醒”——即条件变量可能因为系统原因而返回,但条件并未真正满足。使用谓词判断,就能确保唤醒时条件一定为真。另外,通知线程在修改条件变量关联的布尔标志ready时,也必须在锁的保护下进行,这是标准做法。

4. 使用原子操作(Atomic Operations)

如果共享的操作非常简单,比如只是对一个整数进行加减,那么上锁就显得有点“杀鸡用牛刀”了,性能开销不小。这时候,原子类型就是更好的选择。它在底层通过CPU指令保证操作的不可分割性,既安全又高效。

#include 
#include 
#include 

std::atomic shared_data(0);

void increment() {
    ++shared_data; // 这是一个原子操作
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    t1.join();
    t2.join();
    std::cout << "Shared data: " << shared_data << std::endl;
    return 0;
}

代码简洁多了,不是吗?std::atomic保证了++shared_data这个操作是原子的,无需额外锁。原子操作非常适合计数器、状态标志等简单场景。但要注意,它只能保证单个操作的原子性;如果需要保护多个相关联变量的修改,或者一个复杂的临界区,还是得靠互斥锁。

5. 使用读写锁(Read-Write Lock)

最后来看一种更精细的锁策略:读写锁。它基于一个很常见的观察——读操作往往远多于写操作,而且读操作之间通常不会冲突。读写锁(C++17中的std::shared_mutex)允许多个线程同时读,但写操作是独占的。这能在读多写少的场景下大幅提升并发性能。

#include 
#include 
#include 

std::shared_mutex rw_mtx; // 全局读写锁
int shared_data = 0;

void reader() {
    std::shared_lock lock(rw_mtx); // 上读锁
    std::cout << "Reader thread read shared_data: " << shared_data << std::endl;
}

void writer() {
    std::unique_lock lock(rw_mtx); // 上写锁
    ++shared_data;
    std::cout << "Writer thread wrote shared_data: " << shared_data << std::endl;
}

int main() {
    std::thread t1(reader);
    std::thread t2(writer);
    std::thread t3(reader);
    t1.join();
    t2.join();
    t3.join();
    return 0;
}

这里的关键在于锁的类型:读线程使用std::shared_lock,这意味着多个读锁可以共存;写线程使用std::unique_lock,它和普通的互斥锁一样是独占的。当有写锁存在时,所有读锁和写锁请求都必须等待。这种机制完美适配了配置读取、缓存访问等场景。

好了,以上就是Linux C++并发编程中几种核心的同步机制。从最基础的互斥锁,到应对特殊场景的递归锁,再到用于线程协作的条件变量,以及追求性能极致的原子操作和读写锁,每种工具都有其用武之地。实际开发中,往往需要根据数据访问模式、性能要求和代码复杂度来灵活选择和组合。理解它们的特性和代价,是写出稳健高效并发程序的第一步。

来源:https://www.yisu.com/ask/30673985.html

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
Sublime写前端代码卡顿怎么办?优化Sublime运行速度的几个设置

Sublime写前端代码卡顿怎么办?优化Sublime运行速度的几个设置

Sublime写前端代码卡顿怎么办?优化Sublime运行速度的几个设置 有没有遇到过这种情况?明明只是敲几行代码,Sublime Text却突然变得一卡一卡的,光标移动都带着延迟。别急着怀疑电脑性能,问题很可能出在编辑器本身的设置上。尤其是前端项目,动辄成千上万的依赖文件,对编辑器来说是个不小的负

时间:2026-05-03 10:15
VSCode怎么把选中的大段代码片段一键提取为一个新的函数或方法

VSCode怎么把选中的大段代码片段一键提取为一个新的函数或方法

VSCode Extract Function:选中代码前必须确认三件事 想把一段代码变成独立的函数?VSCode的Extract Function功能确实能一键搞定。但先别急着操作,这个功能可不会猜你的心思,它只认“语法上合法的选中区域”。很多时候操作失败,并不是功能坏了,而是你选中的代码块本身就

时间:2026-05-03 10:15
VSCode远程开发断连_解决SSH连接超时与自动重连

VSCode远程开发断连_解决SSH连接超时与自动重连

根本原因是服务器端sshd空闲超时或防火墙 NAT丢弃连接,需两端配合:服务端启用ClientAliveInterval 60和ClientAliveCountMax 3并重启sshd,客户端检查~ ssh config避免冲突,同时清理VSCode残留进程并更新扩展。 SSH连接被服务器主动断开

时间:2026-05-03 10:15
VSCode项目资源管理器排序_按类型或修改时间排列文件

VSCode项目资源管理器排序_按类型或修改时间排列文件

VSCode资源管理器默认按什么排序? 当你打开VSCode的资源管理器,看着里面一堆文件,有没有好奇过它们的排列顺序?默认情况下,VSCode采用的是最“老实”的规则:按文件名的字母顺序排列,并且不区分大小写。这意味着,index js之所以排在README md前面,仅仅是因为字母“i”在字母表

时间:2026-05-03 10:15
Sublime怎么配置Kotlin环境?Sublime编写Kotlin代码高亮设置

Sublime怎么配置Kotlin环境?Sublime编写Kotlin代码高亮设置

Sublime Text 不能直接运行 Kotlin,但可通过安装 Kotlin 插件实现语法高亮,并配置构建系统调用 kotlinc 和 ja va 编译运行;前提是 JDK 与 Kotlin 编译器已正确安装并加入 PATH,且构建系统中 selector 必须为 source kotlin、-

时间:2026-05-03 10:15
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜
热门教程
更多
  • 游戏攻略
  • 安卓教程
  • 苹果教程
  • 电脑教程