当前位置: 首页 > news >正文

`lock()` 和 `unlock()` 线程同步函数

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.dtcms.com/a/350543.html

相关文章:

  • THM Bricks Heist靶机
  • Java 学习笔记(基础篇10)
  • HTML应用指南:利用POST请求获取全国三星门店位置信息
  • 【目标跟踪】《FastTracker: Real-Time and Accurate Visual Tracking》论文阅读笔记
  • 目前城投债
  • MySQL常见报错分析及解决方案总结(1)---Can‘t connect to MySQL server on ‘localhost‘(10061)
  • Docker:技巧汇总
  • 利用matlab实现CST超表面阵列的自动建模
  • 数据结构:单链表(详解)
  • SSL移动接入方案和移动资源发布
  • 【学习笔记】怎么解决/dev/sda3: clean, XXX files, XXX blocks
  • 【Wrangler(Cloudflare 的官方 CLI)和 npm/npx 的区别一次讲清】
  • SpringCloud微服务技术自用笔记
  • day52_2025-08-25
  • 【猿人学】web第一届 第13题 入门级 cookie
  • 【动态规划】卡特兰数
  • 文件读取结束的判定方法:正确使用feof函数避免文件读取错误
  • CAN总线详解(四)CANFD报文结构
  • 小红书链接uid等批量转换,界面软件工具
  • 【COMSOL】COMSOL帮助文档无法打开的解决办法
  • 智能专网升级:4G与5G混合组网加速企业数字化转型
  • leetcode算法刷题的第十七天
  • 房地产模式的运行逻辑与内在风险
  • Acrobat DC 中的条件格式化
  • 失眠——记录安东尼威廉饮食习惯的250天
  • 推三返一链动模式裂变图解
  • 用Python的Requests+BeautifulSoup爬取微博热搜榜及话题内容
  • SSM从入门到实战:3.1 SpringMVC框架概述与工作原理
  • 音视频学习(五十八):STAP-A模式
  • 编写Linux下usb设备驱动方法:probe函数中要完成的任务