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