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

`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.xdnf.cn/news/18752.html

相关文章:

  • Node.js(1)—— Node.js介绍与入门
  • maven-default-http-blocker (http://0.0.0.0/)
  • 设计模式4-建造者模式
  • 【AI论文】LiveMCP-101:针对支持多主体通信协议(MCP)的智能体在复杂查询场景下的压力测试与故障诊断
  • iptables 防火墙技术详解
  • 【AI编程】如何快速通过AI IDE集成开发工具来生成一个简易留言板系统
  • 使用 HandlerMethodReturnValueHandler 在SpringBoot项目 实现 RESTful API 返回值自动封装,简化开发
  • Linux系统网络管理
  • 积分排行样式
  • 动态住宅代理:跨境电商数据抓取的稳定解决方案
  • 3785定期复盘代码实现设计模式的越识应用
  • Java接口调用第三方接口时的超时处理策略
  • 浅谈为什么尾递归更高效?——从调用栈和汇编的视角
  • 开源零信任本地化部署实战指南:Keycloak + OpenZiti 完整方案
  • 机器学习-朴素贝叶斯
  • 常用的分布式ID设计方案
  • 可信医疗大数据来源、院内数据、病种数据及编程使用方案分析
  • 【MTCNN网络结构记忆卡片】--003nets.py
  • 嵌入式第三十六天(网络编程(TCP))
  • Java的数字计算
  • More Effective C++ 条款06: 区分自增自减操作符的前缀和后缀形式
  • 若依4.7.8(springboot2.5.15)升级到4.8.1(springboot3.3.5)并集成Dubbo3客户端
  • 工程师的自我修养
  • Python JSON数据格式
  • 【数据结构】-4-顺序表(上)
  • 复杂水域场景识别率↑89%!陌讯多模态融合算法在岸边垃圾检测的落地实践
  • CUDA安装,pytorch库安装
  • 小米AX3600访问桥接的光猫
  • 图解SpringMVC工作流程,以及源码分析。
  • Hibernate详解