C++ —— 线程同步(互斥锁)

C++ ------ 线程同步(互斥锁)

线程同步

线程同步:多线程协同工作,协商如何使用共享资源。
C++11线程同步包含三部分内容:

  1. 互斥锁(互斥量)
  2. 条件变量
  3. 生产/消费者模型

互斥锁(互斥量)

只有加锁解锁两种状态,确保同一时间只有一个线程访问共享资源。

访问共享资源之前加锁,加锁成功访问资源,访问完成解锁释放。

若某线程持有锁,其他线程形成等待队列

C++11提供了4种互斥锁:

  • mutex:互斥锁(最常用)
  • timed_mutex:带超时机制的互斥锁
  • recursive_mutex:递归互斥锁
  • recursive_timed_mutex:带超时机制的递归互斥锁

测试代码

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

void func (int n, const string& s) {
    for (int i = 1; i <= 10; i++) {
        cout << "No." << i << ", n = " << n << ", s = " << s << endl;
        this_thread::sleep_for(chrono::seconds(1));
    }
}
a
int main () {
    thread t1(func, 1, "t1");
    thread t2(func, 2, "t2");
    thread t3(func, 3, "t3");
    thread t4(func, 4, "t4");
    thread t5(func, 5, "t5");
    thread t6(func, 6, "t6");

    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();
    t6.join();

    return 0;
}

cout属于全局对象,若多线程都用它向屏幕输出文字,会出乱子。线程越多,越容易乱。

mutex互斥锁

使用互斥锁cout加锁,代码如下:

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex> // 使用互斥锁需要包含的头文件
using namespace std;

mutex mtx; // 创建互斥锁对象,用于保护共享资源cout对象。

void func (int n, const string& s) {
    for (int i = 1; i <= 10; i++) {
        // 每次调用cout对象之前,申请加锁
        mtx.lock();
        cout << "No." << i << ", n = " << n << ", s = " << s << endl;
        // 用完了就解锁
        mtx.unlock();
        this_thread::sleep_for(chrono::seconds(1));
    }
}

int main () {
   // 代码不变...
}

再看一段示例代码:

cpp 复制代码
#include <iostream>
#include <chrono> // 时间相关操作的头文件
#include <thread> // 线程相关操作的头文件
#include <mutex> // 互斥锁的头文件
using namespace std;

int a = 0;
mutex mtx; // 创建互斥锁对象,用于保护共享资源a变量。

void func () {
    for (int i = 0; i < 1000000; ++i) {
        mtx.lock();
        a++;
        mtx.unlock();
    }
}

int main () {
    auto start = chrono::steady_clock::now();
    thread t1(func);
    thread t2(func);

    t1.join();
    t2.join();
    auto end = chrono::steady_clock::now();
    cout << "time = " << chrono::duration_cast<chrono::milliseconds>(end - start).count() << "ms" << endl;
    cout << "a = " << a << endl;

    return 0;
}

运行结果:

time = 101ms

a = 2000000

在代码中添加一些打印信息,能更清楚的观察到:两个线程申请加锁,只有一个成功,另外一个线程等待,直到第一个线程解锁后第二个线程才能加锁。

cpp 复制代码
#include <iostream>
#include <chrono> // 时间相关操作的头文件
#include <thread> // 线程相关操作的头文件
#include <mutex> // 互斥锁的头文件
using namespace std;

int a = 0;
mutex mtx;

void func () {
    for (int i = 0; i < 1000000; ++i) {
        cout << "thread id: " << this_thread::get_id() << ", 申请加锁" << endl;
        mtx.lock();
        cout << "thread id: " << this_thread::get_id() << ", 加锁成功" << endl;
        a++;
        this_thread::sleep_for(chrono::seconds(5));
        mtx.unlock();
        cout << "thread id: " << this_thread::get_id() << ", 解锁" << endl;
        this_thread::sleep_for(chrono::seconds(1));
    }
}

int main () {
    thread t1(func);
    thread t2(func);

    t1.join();
    t2.join();
    
    cout << "a = " << a << endl;

    return 0;
}

运行结果:

thread id: 139727756003072, 申请加锁

thread id: 139727756003072, 加锁成功

thread id: 139727747610368, 申请加锁

thread id: 139727756003072, 解锁

thread id: 139727747610368, 加锁成功

thread id: 139727756003072, 申请加锁

thread id: 139727747610368, 解锁

...

感谢浏览

相关推荐
Q741_1471 小时前
C++ 力扣 76.最小覆盖子串 题解 优选算法 滑动窗口 每日一题
c++·算法·leetcode·双指针·滑动窗口
雷达学弱狗2 小时前
链式法则解释上游梯度应用
开发语言·前端·javascript
小清兔5 小时前
c#基础知识
开发语言·数据库·学习·unity·c#·游戏引擎·.net
奇某人7 小时前
【语法】【C+V】本身常用图表类型用法快查【CSDN不支持,VSCODE可用】
开发语言·vscode·markdown·mermaid
做一位快乐的码农7 小时前
php程序设计之基于PHP的手工艺品销售网站/基于php在线销售系统/基于php在线购物商城系统
开发语言·php
@珍惜一生@7 小时前
Qt开源库
开发语言·qt·开源
Slaughter信仰8 小时前
深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)第四章知识点问答补充及重新排版
java·开发语言·jvm
心灵宝贝8 小时前
Mac用户安装JDK 22完整流程(Intel版dmg文件安装指南附安装包下载)
java·开发语言·macos
secondyoung8 小时前
一文丝滑使用Markdown:从写作、绘图到转换为Word与PPT
开发语言·vscode·编辑器·powerpoint·markdown·visual studio·mermaid
Incredibuild8 小时前
DevSecOps 集成 CI/CD Pipeline:实用指南
c++·ci/cd·devsecops