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”。

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

相关文章
|
7天前
|
算法 安全 C++
【C++入门到精通】互斥锁 (Mutex) C++11 [ C++入门 ]
【C++入门到精通】互斥锁 (Mutex) C++11 [ C++入门 ]
10 0
|
7天前
|
Linux API C++
c++多线程——互斥锁
c++多线程——互斥锁
|
7天前
|
存储 C++
C++ 异常处理机制详解:轻松掌握异常处理技巧
C++ 异常处理提供结构化错误管理,增强程序健壮性。通过`throw`抛出异常,`try-catch`捕获并处理。示例展示了当年龄小于18时抛出异常。优点包括提高健壮性和代码可维护性,但可能降低性能并复杂化代码。另外,介绍了四种在C++中相加两个数的方法,包括使用运算符、函数、类、STL函数和lambda表达式。
21 0
|
7天前
|
安全 编译器 C++
C++从入门到精通:3.2异常处理——掌握C++的异常处理机制,提高程序健壮性
C++从入门到精通:3.2异常处理——掌握C++的异常处理机制,提高程序健壮性
|
7天前
|
存储 C++
C++ 栈和堆的作用机制,及特点区别
在介绍C++中的十分重要的动态内存管理机制之前,有必要先单独来介绍一下C++中的两个概念,分别是栈和堆。
21 2
|
7天前
|
C++
C++11 std::lock_guard 互斥锁
C++11 std::lock_guard 互斥锁
12 0
|
7天前
|
传感器 安全 程序员
【C++多线程 同步机制】:探索 从互斥锁到C++20 同步机制的进化与应用
【C++多线程 同步机制】:探索 从互斥锁到C++20 同步机制的进化与应用
108 1
|
7天前
|
编译器 程序员 C++
【C/C++ 容器操作】C++高效编程:掌握emplace_back与push_back的使用和机制
【C/C++ 容器操作】C++高效编程:掌握emplace_back与push_back的使用和机制
70 0
|
7天前
|
Linux 程序员 C++
【C++ 常见的异步机制】探索现代异步编程:从 ASIO 到协程的底层机制解析
【C++ 常见的异步机制】探索现代异步编程:从 ASIO 到协程的底层机制解析
154 2
|
7天前
|
安全 中间件 编译器
【C/C++ 原子操作】深入浅出:从互斥锁到无锁编程的转变 - 理解C++原子操作和内存模型
【C/C++ 原子操作】深入浅出:从互斥锁到无锁编程的转变 - 理解C++原子操作和内存模型
209 2