本文主要是介绍【C++并发编程】(三)互斥锁:std::mutex,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 互斥锁
- 数据竞争
- C++互斥锁
- lock() 和 unlock()
- std::lock_guard
互斥锁
数据竞争
在并发编程中,数据竞争是指多个线程同时对共享数据进行读写操作,并且至少有一个线程进行写操作,从而导致未定义的行为或结果。
下面给出一个例子:
#include <iostream>
#include <thread> int counter = 0; // 共享数据void increment() { for (int i = 0; i < 100000; ++i) { ++counter; // 修改被共享的数据}
} int main() { std::thread th1(increment); std::thread th2(increment); th1.join(); th2.join(); // 预期输出是200000,但由于数据竞争,实际输出可能小于这个值 std::cout << "Final counter value: " << counter << std::endl; // Final counter value: 107194return 0;
}
两个线程t1
和t2
可能同时对counter
进行自增操作,导致counter
的值增加的数量少于预期(即少于200000)。这是因为:
自增操作包含三个步骤:
- 从内存中读取counter的当前值0。
- 将这个值加1。
- 将结果1写回内存中的counter。
如果两个线程同时执行这些步骤,可能会发生以下情况:
- 线程t1读取counter的值为0。
- 线程t2也读取counter的值为0(因为线程t1还没有将新的值写回内存)。
- 线程t1将1写回counter。
- 线程t2也将1写回counter(因为它之前读取的值是0)。
C++互斥锁
互斥锁(互斥量)能保护多个线程的共享资源不被同时访问。互斥锁的状态只有两种:开锁(unlocked)和闭锁(locked)。当一个任务(或线程)持有互斥锁,且该互斥锁处于闭锁状态,那么这个任务可以执行访问共享资源的代码,其他任务(或线程)则不被允许。开锁后,该线程将失去了对互斥锁的所有权,其他正在等待的线程现在有机会获取这个互斥锁,以访问共享资源。
在C++11中提供了std::mutex
支持这一功能。std::mutex
是一个简单的互斥锁类,它有两个主要操作:lock()
和unlock()
,用于实现闭锁和开锁。
**lock():**当一个线程调用 lock()
函数时,它会尝试获取与该函数关联的互斥锁。如果互斥量当前没有被其他线程锁定(即它是可用的),则调用线程会成功获取锁,并继续执行其后续代码。
如果互斥量已经被其他线程锁定,则调用线程会被阻塞(即它会停止执行,直到锁变得可用)。
一旦线程获取了锁,它就可以安全地访问共享资源,而不必担心其他线程同时修改它。
unlock(): 当一个线程完成对共享资源的访问后,它应该调用 unlock()
函数来释放与该函数关联的互斥量(或其他锁)。释放锁允许其他线程获取该锁并访问共享资源。
在C++中,直接使用 lock()
和 unlock()
函数来管理锁可能会导致一些问题,特别是当代码路径变得复杂时。为了简化锁的管理并减少出错的可能性,C++11引入了 std::lock_guard
和 std::unique_lock
等RAII(Resource Acquisition Is Initialization)风格的包装器,用于自动管理互斥锁的开锁和闭锁。其中,std::lock_guard
是最简单的锁包装器,它只提供了基本的锁定和解锁功能。 std::unique_lock
提供了比 std::lock_guard
更多的功能,更灵活,后续再单独介绍。
下面给出示例展示如何使用lock()
和unlock()
以及锁包装器std::lock_guard
进行互斥锁的闭锁和开锁。
lock() 和 unlock()
#include <iostream>
#include <thread>
#include <mutex> std::mutex mtx; // 创建一个互斥锁实例,用于保护共享数据
int counter = 0; // 共享数据 void increment() { for (int i = 0; i < 100000; ++i) { // 显式地调用了lock()和unlock()来分别获取和释放锁:mtx.lock(); // 获取锁 ++counter; // 递增操作 mtx.unlock(); // 释放锁 }
} int main() { std::thread th1(increment); std::thread th2(increment); th1.join(); th2.join(); std::cout << "Final counter value: " << counter << std::endl; // Final counter value: 200000return 0;
}
std::lock_guard
#include <iostream>
#include <thread>
#include <mutex> std::mutex mtx; // 互斥锁用于保护共享数据
int counter = 0; // 共享数据 void increment() { std::unique_lock<std::mutex> lock(mtx); // 构造时自动获取锁 for (int i = 0; i < 100000; ++i) { ++counter; // 递增操作,因为lock存在,所以此操作是安全的 } // lock在离开作用域时自动释放锁
} int main() { std::thread th1(increment); std::thread th2(increment); th1.join(); th2.join(); std::cout << "Final counter value: " << counter << std::endl; // Final counter value: 200000return 0;
}
这篇关于【C++并发编程】(三)互斥锁:std::mutex的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!