首页 > 编程语言 >C++中的各种锁

C++中的各种锁

时间:2024-11-05 22:33:41浏览次数:2  
标签:std 各种 获取 C++ 互斥 线程 mutex include

在多线程开发中,经常会遇到数据同步,很多情况下用锁都是一个很好的选择。C++中常用的锁主要有下面几种:

互斥锁(std::mutex

  • 这是最基本的一种锁。它用于保护共享资源,在任意时刻,最多只有一个线程可以获取该锁,从而访问被保护的资源。当一个线程获取了互斥锁后,其他试图获取该锁的线程会被阻塞,直到持有锁的线程释放它。
  • 例如,在一个多线程程序中,如果多个线程需要访问和修改同一个全局变量,就可以使用互斥锁来确保在同一时间只有一个线程能够进行修改操作,避免数据竞争导致的错误结果。
 1 #include <iostream>
 2 #include <mutex>
 3 #include <thread>
 4 
 5 std::mutex m;
 6 int counter = 0;
 7 
 8 void increment() {
 9     m.lock();
10     counter++;
11     std::cout << "Counter value in thread " << std::this_thread::get_id() << " is " << counter << std::endl;
12     m.unlock();
13 }
14 
15 int main() {
16     std::thread t1(increment);
17     std::thread t2(increment);
18     t1.join();
19     t2.join();
20     return 0;
21 }
 

递归互斥锁(std::recursive_mutex

  • 递归互斥锁允许同一个线程多次获取该锁。它内部会记录锁的获取次数,每获取一次,计数加 1,每次释放锁时,计数减 1,当计数为 0 时,锁才真正被释放,可供其他线程获取。
  • 假设在一个复杂的函数调用链中,函数 A 调用函数 B,函数 B 又调用函数 A,并且这些函数都需要访问同一个受保护的资源。如果使用普通互斥锁,就会出现死锁,而递归互斥锁就可以避免这种情况,因为它允许同一线程多次获取锁。
 1 #include <iostream>
 2 #include <mutex>
 3 #include <thread>
 4 
 5 std::recursive_mutex rm;
 6 
 7 void recursiveFunction(int count) {
 8     rm.lock();
 9     if (count > 0) {
10         std::cout << "Recursive call with count = " << count << std::endl;
11         recursiveFunction(count - 1);
12     }
13     rm.unlock();
14 }
15 
16 int main() {
17     std::thread t(recursiveFunction, 3);
18     t.join();
19     return 0;
20 }

 

读写锁(std::shared_mutex) C++17开始才有

  • 读写锁主要用于区分对共享资源的读操作和写操作。它有两种获取模式:共享模式(读模式)和独占模式(写模式)。
  • 多个线程可以同时以共享模式获取读写锁,这意味着它们可以同时读取共享资源,而不会相互干扰。但是,当一个线程要以独占模式获取读写锁(进行写操作)时,其他线程(无论是读操作还是写操作)都不能获取该锁,直到写操作完成并释放锁。这种机制在有大量读操作和少量写操作的场景下,可以提高程序的并发性能。例如,在一个缓存系统中,多个线程可能经常读取缓存中的数据,只有在缓存数据需要更新时才会进行写操作,使用读写锁可以很好地处理这种情况。 
 1 #include <iostream>
 2 #include <shared_mutex>
 3 #include <thread>
 4 #include <vector>
 5 
 6 std::shared_mutex smtx;
 7 int shared_data = 0;
 8 
 9 void read_data() {
10     std::shared_lock<std::shared_mutex> lock(smtx);
11     std::cout << "Read data: " << shared_data << std::endl;
12 }
13 
14 void write_data(int new_value) {
15     std::unique_lock<std::shared_mutex> lock(smtx);
16     shared_data = new_value;
17     std::cout << "Wrote data: " << shared_data << std::endl;
18 }
19 
20 int main() {
21     std::vector<std::thread> read_threads;
22     for (int i = 0; i < 5; i++) {
23         read_threads.push_back(std::thread(read_data));
24     }
25     std::thread write_thread(write_data, 10);
26     for (auto& t : read_threads) {
27         t.join();
28     }
29     write_thread.join();
30     return 0;
31 }

 

定时互斥锁(std::timed_mutex

  • 定时互斥锁是std::mutex的扩展。除了具备std::mutex的基本功能外,它还允许线程在尝试获取锁时设置一个超时时间。
  • 如果在规定的超时时间内无法获取锁,线程不会一直等待,而是可以执行其他操作或者返回错误信息。这在一些对时间敏感的场景中非常有用,比如在一个实时系统中,线程不能因为等待一个锁而无限期地阻塞,需要在一定时间后放弃获取并进行其他处理。 
 1 #include <iostream>
 2 #include <chrono>
 3 #include <thread>
 4 #include <mutex>
 5 
 6 std::timed_mutex tm;
 7 
 8 void tryLockFunction() {
 9     if (tm.try_lock_for(std::chrono::seconds(1))) {
10         std::cout << "Acquired lock" << std::endl;
11         std::this_thread::sleep_for(std::chrono::seconds(2));
12         tm.unlock();
13     } else {
14         std::cout << "Could not acquire lock in time" << std::endl;
15     }
16 }
17 
18 int main() {
19     std::thread t1(tryLockFunction);
20     std::thread t2(tryLockFunction);
21     t1.join();
22     t2.join();
23     return 0;
24 }

 

递归定时互斥锁(std::recursive_timed_mutex

  • 这是结合了递归互斥锁和定时互斥锁特点的一种锁。它允许同一线程多次获取锁,并且在获取锁时可以设置超时时间。
  • 当一个线程多次获取这种锁后,需要释放相同次数的锁,锁才会真正被释放,并且在获取锁的过程中,如果在超时时间内无法获取,线程可以采取相应的措施。 
 1 #include <iostream>
 2 #include <chrono>
 3 #include <thread>
 4 #include <mutex>
 5 
 6 std::recursive_timed_mutex rtm;
 7 
 8 void recursiveTryLockFunction(int count) {
 9     if (rtm.try_lock_for(std::chrono::seconds(1))) {
10         std::cout << "Recursive acquired lock, count = " << count << std::endl;
11         if (count > 0) {
12             recursiveTryLockFunction(count - 1);
13         }
14         rtm.unlock();
15     } else {
16         std::cout << "Could not recursively acquire lock in time" << std::endl;
17     }
18 }
19 
20 int main() {
21     std::thread t(recursiveTryLockFunction, 3);
22     t.join();
23     return 0;
24 }

 

自旋锁(通常用std::atomic_flag实现)

  • 自旋锁是一种忙等待的锁机制。当一个线程尝试获取自旋锁而锁已经被占用时,这个线程不会进入阻塞状态,而是会不断地检查(“自旋”)锁是否已经被释放。
  • 自旋锁在等待时间较短的情况下可能会有比较好的性能表现,因为它避免了线程切换的开销。但是,如果等待时间过长,由于线程一直在占用 CPU 资源进行检查,会导致 CPU 资源的浪费。一般在底层代码或者对性能要求极高、等待时间预计很短的场景下使用。 
 1 #include <iostream>
 2 #include <atomic>
 3 #include <thread>
 4 
 5 std::atomic_flag spinLock = ATOMIC_FLAG_INIT;
 6 
 7 void criticalSection() {
 8     while (spinLock.test_and_set()) {
 9         // 自旋等待
10     }
11     std::cout << "Entered critical section" << std::endl;
12     // 临界区操作
13     spinLock.clear();
14 }
15 
16 int main() {
17     std::thread t1(criticalSection);
18     std::thread t2(criticalSection);
19     t1.join();
20     t2.join();
21     return 0;
22 }

 

条件变量(std::condition_variable)配合互斥锁用于同步(严格来说条件变量不是锁,但常一起用于线程同步场景)

  • 条件变量本身不是一种锁,但它通常和互斥锁一起使用,用于实现线程间的同步。它可以让一个线程等待某个条件满足后再继续执行。
  • 例如,一个生产者 - 消费者模型中,消费者线程在缓冲区为空时可以使用条件变量等待,直到生产者线程生产出产品并通知消费者线程,这个过程中互斥锁用于保护缓冲区这个共享资源,条件变量用于实现线程间的通信和同步。
 1 #include <iostream>
 2 #include <thread>
 3 #include <mutex>
 4 #include <condition_variable>
 5 #include <queue>
 6 
 7 std::mutex mtx;
 8 std::condition_variable cv;
 9 std::queue<int> buffer;
10 const int bufferSize = 5;
11 
12 void producer() {
13     for (int i = 0; i < 10; ++i) {
14         std::unique_lock<std::mutex> lock(mtx);
15         while (buffer.size() == bufferSize) {
16             cv.wait(lock);
17         }
18         buffer.push(i);
19         std::cout << "Produced: " << i << std::endl;
20         cv.notify_all();
21     }
22 }
23 
24 void consumer() {
25     for (int i = 0; i < 10; ++i) {
26         std::unique_lock<std::mutex> lock(mtx);
27         while (buffer.empty()) {
28             cv.wait(lock);
29         }
30         int data = buffer.front();
31         buffer.pop();
32         std::cout << "Consumed: " << data << std::endl;
33         cv.notify_all();
34     }
35 }
36 
37 int main() {
38     std::thread producerThread(producer);
39     std::thread consumerThread(consumer);
40     producerThread.join();
41     consumerThread.join();
42     return 0;
43 }

 



标签:std,各种,获取,C++,互斥,线程,mutex,include
From: https://www.cnblogs.com/Arthurian/p/18526135

相关文章

  • 7.10 已知一组观测数据,如表中7.17.excel(表中第一列为x的值,第二列为y的值)。试用插值方
    importnumpyasnpimportpandasaspdimportmatplotlib.pyplotaspltfromscipy.interpolateimportinterp1d,PchipInterpolator,CubicSplinefromscipy.optimizeimportcurve_fitfromscipy.statsimportnormfile_path='7.17.xlsx'data=pd.rea......
  • ROS机器人编程<六>:了解ROS系统及使用VScode实现话题通信(C++)
    目录ROS中基本的通信机制:一、话题通信:1.话题通信定义与基本概念2.核心要素3.工作流程4.消息接口与数据类型二、vscode实现话题通信三、C++实现话题通信 要求:编写发布订阅实现,要求发布方以10HZ(每秒10次)的频率发布文本消息,订阅方订阅消息并将消息内容打印输出1.在......
  • c++:智能指针
    文章目录前言一、内存泄漏1.1内存泄漏的定义1.2内存泄漏的常见原因1.3内存泄漏的危害二、智能指针的用法和模拟实现2.1RAII2.1.1RAII的工作原理2.1.2RAII的优点2.2智能指针的原理和设计思路2.3智能指针的种类和特点2.3.1std::auto_ptr2.3.2std::unique_ptr2.3......
  • 什么是C++模板,有哪些类型的模板?
    模板C++模板是一种强大的语言特性,允许开发者编写与类型无关的代码,从而实现代码的复用和灵活性。通过模板,可以定义函数和类,具体实现将由具体的类型实例化决定。函数模板函数模板(FunctionTemplates):函数模板用于定义一个通用的函数,该函数可以接受任意类型的参数。通过使用模......
  • c++知识及编译调试
    文章目录c++知识指针查找内存模型引用函数编译调试1.编译选项2.静态库和动态库3.gdb调试代码1.通讯录c++知识指针常量(的)指针constint*p=&a;指针指向可改,指向的值不可改。指针常量int*constp=&a;指针不能改,指向的值可改。查找unordered_setit!=uset.e......
  • C++20 STL CookBook 4:使用range在容器中创建view
    目录rangeviewrange_adaptor的三个概念以std::string和std::string_view为例子初次入手补充ranges的一些操作rangeviewrange_adaptor的三个概念新的范围库是C++20中更重要的新增功能之一。它为过滤和处理容器提供了新的范例。范围为更有效和可读的代码提供了简......
  • (分享源码)计算机毕业设计必看必学 上万套实战教程手把手教学JAVA、PHP,node.js,C++、pyth
    摘要信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题。针对校园疫情大学生防控出入系统等问题,对校园疫情大学生防控出入系统进行研究分析,然后开发设计......
  • C++——用指向指针的指针的方法对5个字符串排序并输出。
    没注释的源代码#include<iostream>#include<string.h>usingnamespacestd;voidsort(char**p);intmain(){  constintm=20;  char**p,*pstr[5],str[5][m];  for(inti=0;i<5;i++)    pstr[i]=str[i];  cout<<"pleaseinput5......
  • 计算机毕业设计项目推荐,高校学生社团管理系统 00498(开题答辩+程序定制+全套文案 )上万
    摘要随着计算机科学技术的日渐成熟,人们已经深刻地认识到了计算机在各个领域中发挥的功能的强大,计算机已经进入到了人类社会发展的各个领域,并且发挥着十分重要的作用。目前学校学生社团的管理是一项系统而复杂的工作,它需要一个团队互相配合、分工协作。在该领域,传统的手工存取......
  • C++——用指向指针的指针的方法对n个整数排序并输出。要求将排序单独写成一个团数,整数
    没注释的源代码#include<iostream>usingnamespacestd;voidsortNumbers(int**arr,intn);intmain(){  intn;  cout<<"enterthenumberofintegers:";  cin>>n;  int**arr=newint*[n];  for(inti=0;i<n;i++)  {......