1) 函数的概念与用途

lock()unlock() 不是特定的标准库函数,而是线程同步原语的一般概念,用于在多线程环境中保护共享资源。在不同的编程环境和库中,这些函数有不同的具体实现(如 POSIX 线程的 pthread_mutex_lock() 或 C++ 的 std::mutex::lock())。

可以将 lock()unlock() 想象成"资源门的钥匙":当一个线程需要访问共享资源时,它必须先获取锁(拿到钥匙),使用完资源后释放锁(归还钥匙)。这样可以确保同一时间只有一个线程能访问受保护的资源,防止数据竞争和不一致。

典型应用场景包括:

  • 共享数据保护:保护多线程共享的变量、数据结构
  • 临界区控制:确保代码关键部分只能由一个线程执行
  • 资源访问序列化:对有限资源(如文件、网络连接)的有序访问
  • 生产者-消费者模式:协调生产者和消费者线程之间的数据交换

2) 函数的声明与出处

锁的实现因平台和编程语言而异,以下是几种常见实现:

POSIX 线程 (pthread)

#include <pthread.h>int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

C++ 标准库

#include <mutex>std::mutex mtx;
mtx.lock();    // 获取锁
mtx.unlock();  // 释放锁

Windows API

#include <windows.h>CRITICAL_SECTION cs;
EnterCriticalSection(&cs);   // 获取锁
LeaveCriticalSection(&cs);   // 释放锁

3) 参数详解:互斥锁对象

对于 POSIX 线程的 pthread_mutex_lock/unlock

  • pthread_mutex_t *mutex
    • 作用:指向要锁定/解锁的互斥锁对象的指针
    • 要求:互斥锁必须已通过 pthread_mutex_init() 初始化
    • 注意:不同类型的互斥锁有不同的特性(普通、递归、错误检查等)

4) 返回值:操作状态

对于 POSIX 线程的 pthread_mutex_lock/unlock

  • 返回值类型int
  • 返回值含义
    • 成功:返回 0
    • 失败:返回错误码(如 EINVAL 无效参数,EDEADLK 死锁等)

5) 实战演示:多种使用场景

示例 1:POSIX 线程保护共享变量

#include <stdio.h>
#include <pthread.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int shared_counter = 0;void* thread_function(void* arg) {for (int i = 0; i < 100000; i++) {// 获取锁pthread_mutex_lock(&mutex);// 临界区开始shared_counter++;// 临界区结束// 释放锁pthread_mutex_unlock(&mutex);}return NULL;
}int main() {pthread_t thread1, thread2;pthread_create(&thread1, NULL, thread_function, NULL);pthread_create(&thread2, NULL, thread_function, NULL);pthread_join(thread1, NULL);pthread_join(thread2, NULL);printf("Final counter value: %d (expected: 200000)\n", shared_counter);pthread_mutex_destroy(&mutex);return 0;
}

示例 2:C++ 使用 std::mutex

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>std::mutex mtx;
int shared_value = 0;void increment() {for (int i = 0; i < 100000; i++) {mtx.lock();        // 获取锁shared_value++;    // 临界区mtx.unlock();      // 释放锁}
}int main() {std::vector<std::thread> threads;for (int i = 0; i < 10; i++) {threads.emplace_back(increment);}for (auto& t : threads) {t.join();}std::cout << "Final value: " << shared_value << " (expected: 1000000)" << std::endl;return 0;
}

示例 3:使用 RAII 避免忘记解锁 (C++)

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>std::mutex mtx;
int shared_value = 0;void increment() {for (int i = 0; i < 100000; i++) {// 使用 std::lock_guard 自动管理锁生命周期std::lock_guard<std::mutex> lock(mtx);shared_value++; // 临界区// lock 析构时自动释放锁}
}int main() {std::vector<std::thread> threads;for (int i = 0; i < 10; i++) {threads.emplace_back(increment);}for (auto& t : threads) {t.join();}std::cout << "Final value: " << shared_value << " (expected: 1000000)" << std::endl;return 0;
}

6) 编译方式与注意事项

编译 POSIX 线程程序:

gcc -o lock_demo lock_demo.c -lpthread

编译 C++ 程序:

g++ -o lock_demo lock_demo.cpp -std=c++11 -lpthread

关键注意事项:

  1. 死锁风险:不正确使用锁可能导致死锁(多个线程互相等待对方释放锁)
  2. 性能影响:过度使用锁会降低程序性能,应尽量减少锁的持有时间
  3. 锁粒度:选择适当的锁粒度(粗粒度减少开销但降低并发性,细粒度提高并发性但增加开销)
  4. 异常安全:在 C++ 中使用 RAII 模式(如 std::lock_guard)确保异常时锁能被正确释放
  5. 锁类型选择:根据需求选择合适的锁类型(普通锁、递归锁、读写锁等)

7) 执行结果说明

示例 1 输出:

Final counter value: 200000 (expected: 200000)

展示了如何使用互斥锁保护共享计数器,确保两个线程各增加 100,000 次后结果为 200,000。

示例 2 输出:

Final value: 1000000 (expected: 1000000)

显示了 C++ 中使用 std::mutex 保护共享变量,10 个线程各增加 100,000 次后结果为 1,000,000。

示例 3 输出:

Final value: 1000000 (expected: 1000000)

演示了使用 RAII 模式(std::lock_guard)自动管理锁的生命周期,避免忘记解锁。

8) 总结:锁的工作流程与价值

锁的工作流程可以总结如下:

线程尝试获取锁 lock()
锁是否可用?
获取锁, 进入临界区
线程阻塞等待
执行临界区代码
访问共享资源
释放锁 unlock()
其他线程可以竞争锁

锁是多线程编程中的核心同步机制,它的价值在于:

  1. 数据一致性:防止多个线程同时修改共享数据导致的不一致
  2. 执行有序性:确保临界区代码的原子性执行
  3. 线程协调:协调多个线程对有限资源的访问
多线程同步需求
如何选择同步机制?
保护共享数据
使用互斥锁 Mutex
读写分离场景
使用读写锁 R/W Lock
线程间通知
使用条件变量 Condition Variable
一次性初始化
使用 once_flag

最佳实践建议:

  1. 最小化临界区:尽量减少锁的持有时间,只保护真正需要同步的代码
  2. 避免嵌套锁:尽量避免在持有锁时获取其他锁,防止死锁
  3. 使用RAII模式:在C++中使用std::lock_guardstd::unique_lock自动管理锁
  4. 锁顺序一致:如果必须使用多个锁,确保所有线程以相同顺序获取它们
  5. 考虑替代方案:根据场景考虑使用无锁编程、原子操作或其他同步机制

lock()unlock() 是多线程编程的基础工具,正确使用它们对于编写线程安全、高效并发的程序至关重要。掌握锁的原理、使用方法和注意事项,可以帮助开发者避免常见的多线程问题,如数据竞争、死锁和性能瓶颈。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/94304.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/94304.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/94304.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

升级openssh后ORACLE RAC EM 安装失败处理

升级过程中由于SCP传输时目标目录/tmp/tempRACTrans_2025_08_22--18-25-44-032/ractrans 不存在导致的OC4J配置失败&#xff1a;WARNING: /usr/bin/scp: dest open "/tmp/tempRACTrans_2025_08_22--18-25-44-032/ractrans": No such file or directory/usr/bin/scp…

ADB 调试工具的学习[特殊字符]

一、ADB 的工作原理 1.1 ADB 概念 ADB (Android Debug Bridge)&#xff1a;Android 调试桥&#xff0c;是开发/测试 Android 应用必备的调试工具。作用&#xff1a;通过 电脑终端命令 操作 安卓手机/模拟器。 1.2 ADB 构成与原理 ADB 由三部分组成&#xff1a; Client 端&#…

用一根“数据中枢神经”串起业务从事件流到 Apache Kafka

1. 为什么是“事件流”&#xff1f; 在一个软件定义、自动化、永远在线的世界里&#xff0c;系统之间最需要的是&#xff1a;把发生了什么这件事&#xff0c;第一时间、按正确顺序、可靠地传到该知道的人/系统那里。 事件流就像企业的中枢神经&#xff1a;它把数据库更新、设备…

【RAGFlow代码详解-4】数据存储层

数据库基础设施 RAGFlow 使用关系数据库&#xff08;MySQL 或 PostgreSQL&#xff09;作为主要元数据存储&#xff0c;通过具有连接池和重试机制的 Peewee ORM 进行管理。 连接管理 数据库连接通过 service_conf.yaml 和环境变量进行配置。该系统支持具有可配置连接池的 MySQL …

ES_映射

一、 映射&#xff08;Mapping&#xff09;是什么&#xff1f; 简单来说&#xff0c;映射就像是关系型数据库中的表结构定义&#xff08;Schema&#xff09;。它定义了索引&#xff08;Index&#xff09;中的文档&#xff08;Document&#xff09;可以包含哪些字段&#xff08;…

【Linux | 网络】多路转接IO之poll

一、poll函数二、poll的优缺点三、实现poll服务器&#xff08;只关心读事件&#xff09;3.1 Log.hpp&#xff08;日志&#xff09;3.2 Lockguard.hpp&#xff08;自动管理锁&#xff09;3.3 Socket.hpp&#xff08;封装套接字&#xff09;3.4 PollServer.hpp&#xff08;服务端…

一站式资源共享平台模板,助力快速搭建专属资源站源码

内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 这个资源分享网站模板是一个功能完整、设计现代的单页网站&#xff0c;非常适合快速搭建资源分享平台。以下是关于这个模板的详细介绍&#xff0c;帮助你更好地理解并发布到自己的网站&a…

ngnix的部分配置

1. 禁止特定IP地址访问你可以通过在Nginx配置文件中添加deny指令来阻止特定IP地址或IP地址段的访问。server {listen 80;server_name example.com;location / {deny 192.168.1.0/24;allow all;} }2. 允许特定IP地址访问如果你想允许只有特定IP地址或IP地址段的访问&#xff0c;…

Qwt7.0-打造更美观高效的Qt开源绘图控件库

概述 Qt 生态里能画图的库不多&#xff0c;主流的为QCustomPlot、Qwt、Qt Charts和KDChart&#xff0c;Qt6.8之后把原来的 Qt Charts&#xff08;2D&#xff09; 与 Qt DataVisualization&#xff08;3D&#xff09; 合并为统一的Qt Graphs模块&#xff08;注意不是Qt Graphic…

NFC线圈设计计算

对工作于13.56MHz的电感耦合的NFC系统,针对小距离的传统天线通常是环形或者矩形的扁平线圈。 圆形扁平线圈计算评估 对于二阶估计,我们可以由匝数决定的电感等式为 考虑到线圈的物理参数,设置平均直径:D_averD0-N(gw) 线圈周长: &#xff1b;d2*(w t)/π 初始设置中的这种电感…

mac设置鼠标滚轮方向

mac中滚轮的滑动方向和windows是相反的&#xff0c;如果需要设置和windows相同&#xff0c;设置如下&#xff1a;将自然滚动关闭即可。

QSpinBox的用法及其使用QSS对其美化

摘要 在现代应用程序开发中&#xff0c;提供一个直观且用户友好的界面至关重要。Qt框架提供了丰富的控件和工具&#xff0c;帮助开发者实现这一目标。本文将详细介绍如何使用Qt的QSpinBox控件让用户输入数值&#xff0c;并通过Qt Style Sheets (QSS) 美化界面&#xff0c;提升…

18 继续学习

要设计出一个好的系统&#xff0c;需要多年的知识积累。有一个捷径是研究真实世界的系统架构。本文将介绍一些有帮助的阅读材料。 务必留意那些真实系统之间共通的原理和相同的底层技术。研究每个技术并了解它解决了什么问题&#xff0c; 这是一个巩固基础知识和完善设计过程的…

深度学习篇---混淆矩阵

要理解混淆矩阵&#xff08;Confusion Matrix&#xff09;&#xff0c;我们可以从它的名字入手&#xff1a;它本质是一张 “帮你理清模型预测结果到底‘混淆’在哪里” 的表格&#xff0c;核心作用是评估分类模型的表现 —— 比如判断一张图片是 “猫” 还是 “狗”、一封邮件是…

MySQL重大隐患!mysqlpump的--set-gtid-purged参数在5.7和8.0的雷区

MySQLPump是MySQL官方提供的一个用于备份和恢复MySQL数据库的工具。它于MySQL 5.7.8版本中首次引入&#xff0c;旨在提供一种快速、可靠且高效的备份和恢复解决方案。MySQL Pump首次支持了并行导出、压缩导出&#xff0c;可以利用多核CPU来提高备份能力&#xff0c;在效率上要比…

低质量视频变高清AI:告别模糊,重现清晰画质

在数字时代&#xff0c;视频内容的创作和消费日益普及&#xff0c;然而&#xff0c;许多早期拍摄或存储的视频&#xff0c;由于技术限制或压缩等原因&#xff0c;往往存在画质不佳的问题&#xff0c;如模糊、噪点多、分辨率低等。这不仅影响观看体验&#xff0c;也限制了这些珍…

Linux入门教程 第十二章 防火墙

文章目录前言一、 iptables 概述Netfilter二、iptables 的表、链结构2.1 ptables的四表五链结构介绍2.1.1 四表五链2.1.2 四表2.1.3 **五链**2.2 数据包过滤的匹配流程&#xff08;数据包到防火墙&#xff09;2.2.1 规则链之间的匹配顺序:主机型防火墙:2.2.2 规则链内的匹配顺序…

单词搜索+回溯法

题目&#xff1a;思考&#xff1a; 1.经典回溯 实现&#xff1a; class Solution { public:bool find_word(vector<vector<char>>&board,string word,int pos,int i,int j){bool retfalse;if (posword.size()-1) return board[i][j]word[pos];if (board[i][j…

【嵌入式开发 Linux 常用命令系列 8 -- git checkout 解冲突详细介绍】

文章目录1. Git 冲突产生的场景2. 冲突标记符号解释3. git checkout --ours 和 git checkout --theirs语法含义使用场景4. 操作完成后的流程5. 举例演示1. Git 冲突产生的场景 当你在 git merge、git rebase、git cherry-pick 等操作时&#xff0c;如果 同一个文件的同一部分在…

16-day13强化学习和训练大模型

强化学习 强化学习和监督学习是机器学习中的两种不同的学习范式 强化学习&#xff1a;目标是让智能体通过与环境的交互&#xff0c;学习到一个最优策略&#xff0c;以最大化长期累积奖励。 例如&#xff0c;在机器人导航任务中&#xff0c;智能体需要学习如何在复杂环境中移动&…