C++中的锁机制

简介: C++中的锁机制

1.介绍

加锁是一种用于保护临界区的方法。它的基本思想是使用一个锁来控制对临界区的访问。当一个线程需要进入临界区时,它必须先获得锁;当它离开临界区时,它必须释放锁。如果锁已经被其他线程占用,那么当前线程将被阻塞,直到锁被释放。

C++11 引入了多线程支持,包括对锁的支持。C++11 中提供了多种类型的锁,包括 `std::mutex`、`std::recursive_mutex`、`std::timed_mutex` 和 `std::recursive_timed_mutex`。

`std::mutex` 是最基本的一种锁,它提供了两个方法:`lock()` 和 `unlock()`。当一个线程需要进入临界区时,它可以调用 `lock()` 方法来获得锁;当它离开临界区时,它必须调用 `unlock()` 方法来释放锁。

2.例子

下面是一个简单的示例:

#include <iostream>
#include <mutex>
#include <thread>
std::mutex mtx;
void print() {
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << "Hello from thread " << std::this_thread::get_id() << std::endl;
}
int main() {
    std::thread t1(print);
    std::thread t2(print);
    t1.join();
    t2.join();
    return 0;
}

在这个示例中,我们定义了一个全局的 `std::mutex` 对象 `mtx` 来保护对 `std::cout` 的访问。在 `print()` 函数中,我们使用了 `std::lock_guard` 来自动管理锁的生命周期。当 `std::lock_guard` 对象创建时,它会自动调用 `mtx.lock()` 来获得锁;当它销毁时,它会自动调用 `mtx.unlock()` 来释放锁。

这样,当两个线程同时调用 `print()` 函数时,只有一个线程能够获得锁并进入临界区,另一个线程将被阻塞。这样就避免了数据竞争和不一致的结果。

`t1.join()` 和 `t2.join()` 这两行代码分别用于等待线程 `t1` 和 `t2` 结束。`join()` 方法会阻塞当前线程,直到被调用的线程结束。

在上面的示例中,`main()` 函数中创建了两个线程 `t1` 和 `t2`,它们都执行 `print()` 函数。当 `main()` 函数执行到 `t1.join()` 时,它会阻塞并等待线程 `t1` 结束。当线程 `t1` 结束后,`main()` 函数继续执行,并调用 `t2.join()` 来等待线程 `t2` 结束。当线程 `t2` 结束后,`main()` 函数继续执行,并最终退出。

使用 `join()` 方法可以确保在线程结束之前不会退出程序。如果不调用 `join()` 方法,那么主线程可能会在其他线程结束之前退出,导致未定义的行为

这段代码创建了两个线程 `t1` 和 `t2`,它们都执行 `print()` 函数。由于我们使用了锁来保护对 `std::cout` 的访问,所以两个线程的输出不会交叉。因此,这段代码的一个可能的输出结果是:

Hello from thread 1
Hello from thread 2

或者:

Hello from thread 2
Hello from thread 1

具体的输出顺序取决于线程的调度顺序,这是不确定的。

`std::thread t1(print);` 和 `std::thread t2(print);` 这两行代码分别创建了两个线程 `t1` 和 `t2`。它们都执行 `print()` 函数,但是它们是两个独立的线程,它们之间没有直接的关系。除了执行相同的函数之外,它们没有其他区别。

如果不加锁,那么两个线程可能会同时访问 `std::cout`,导致输出结果交叉。例如,一个可能的输出结果是:

HeHlleollo  ffrroomom  tthhrreeaadad  11
Hello from thread 2

由于线程的调度顺序是不确定的,所以输出结果也是不确定的。在实际应用中,不加锁可能会导致数据竞争和不一致的结果,因此应该避免这种情况。

“HeHlleollo ffrroomom tthhrreeaadad 11” 是一个可能的输出结果,用来说明当两个线程同时访问 `std::cout` 时可能会发生什么。

在这个例子中,两个线程同时执行 `print()` 函数,它们都试图向 `std::cout` 输出字符串 “Hello from thread X”(其中 X 是线程的 ID)。由于没有加锁来保护对 `std::cout` 的访问,所以两个线程的输出可能会交叉。

例如,线程 1 可能输出了 “H”,然后线程 2 输出了 “H”,然后线程 1 输出了 “e”,然后线程 2 输出了 “e”,以此类推。最终的结果就是 “HeHlleollo ffrroomom tthhrreeaadad 11”。

需要注意的是,这只是一个可能的输出结果。实际的输出结果取决于线程的调度顺序和操作系统的实现细节,它是不确定的。

相关文章
|
1月前
|
存储 安全 编译器
【c++】深入理解别名机制--引用
本文介绍了C++中的引用概念及其定义、特性、实用性和与指针的区别。引用是C++中的一种别名机制,通过引用可以实现类似于指针的功能,但更安全、简洁。文章详细解释了引用的定义方式、引用传参和返回值的应用场景,以及常引用的使用方法。最后,对比了引用和指针的异同,强调了引用在编程中的重要性和优势。
39 1
|
2月前
|
安全 测试技术 C++
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化2
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化
77 6
|
2月前
|
安全 测试技术 C++
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化1
【C++篇】从零实现 C++ Vector:深度剖析 STL 的核心机制与优化
91 7
|
3月前
|
Linux API C++
超级好用的C++实用库之互斥锁
超级好用的C++实用库之互斥锁
33 2
|
7月前
|
安全 C++
C++中的异常处理与错误处理机制
C++中的异常处理与错误处理机制
87 0
|
7月前
|
Linux API C++
c++多线程——互斥锁
c++多线程——互斥锁
|
7月前
|
C++
C++11 std::lock_guard 互斥锁
C++11 std::lock_guard 互斥锁
65 0
|
5月前
|
C++ 运维
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
77 2
|
6月前
|
安全 C++
C++一分钟之-互斥锁与条件变量
【6月更文挑战第26天】在C++并发编程中,`std::mutex`提供互斥访问,防止数据竞争,而`std::condition_variable`用于线程间的同步协调。通过`lock_guard`和`unique_lock`防止忘记解锁,避免死锁。条件变量需配合锁使用,确保在正确条件下唤醒线程,注意虚假唤醒和无条件通知。生产者-消费者模型展示了它们的应用。正确使用这些工具能解决同步问题,提升并发性能和可靠性。
69 4
|
7月前
|
安全 Linux 编译器
从C语言到C++_40(多线程相关)C++线程接口+线程安全问题加锁(shared_ptr+STL+单例)(下)
从C语言到C++_40(多线程相关)C++线程接口+线程安全问题加锁(shared_ptr+STL+单例)
58 0