ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【C++】验证STL容器线程不安全 [打印本页]

作者: 万有斥力    时间: 2024-12-10 10:21
标题: 【C++】验证STL容器线程不安全
概要

在并发编程中,线程安满是确保多个线程在同时访问共享资源时,不会引起数据竞争或意外的举动。在C++中,std::vector通常并不是线程安全的,因此在多线程环境中对std::vector举行读写操作大概会导致未界说举动。本文将通过实例验证std::vector的线程不安全性,并讨论如何解决这一题目。
整体架构流程

本文将分以下几部分讲解如何验证std::vector的线程不安全性:
1、多线程操作std::vector示例代码:构建一个简朴的C++代码示例,在多线程中对std::vector执行插入和读取操作。
2、题目分析:分析代码在运行时出现的线程题目,分析出现的原因
3、解决方法:介绍一下解决的方法。
技术名词表明

线程安全(Thread Safety):指在多线程步伐中,多个线程访问共享资源时,不会产生数据竞争或未界说举动。
数据竞争(Data Race):多个线程并发访问共享资源,并且至少一个线程执行写操作时,发生的竞争征象。
锁(Mutex):一种同步机制,用于防止多个线程同时访问共享资源。
技术细节

示例代码

以下代码展示了一个多线程环境中对std::vector举行读写操作的示例,验证其线程不安全性。
  1. #include <iostream>
  2. #include <pthread.h>
  3. #include <string>
  4. #include <vector>
  5. #include <unistd.h>
  6. using namespace std;
  7. class Thread
  8. {
  9. public:
  10.     Thread(string &name) : _threadname(name) {}
  11.     bool start()
  12.     {
  13.         sleep(1);//这里sleep是为了让现象变得固定,不加sleep,则现象就太随机,不容易画图分析
  14.         int n = pthread_create(&_tid, nullptr, threadroutine, this);
  15.         if (n == 0)
  16.         {
  17.             return true;
  18.         }
  19.         return false;
  20.     }
  21.     static void *threadroutine(void *args)
  22.     {
  23.       
  24.         Thread *self = static_cast<Thread *>(args);
  25.         cout << self->_threadname << endl;
  26.         return nullptr;
  27.     }
  28.     void Join()
  29.     {
  30.         pthread_join(_tid, nullptr);
  31.     }
  32. private:
  33.     pthread_t _tid;
  34.     std::string _threadname;
  35. };
  36. int main()
  37. {
  38.     vector<Thread> threads;
  39.     for (int i = 0; i < 10; i++)
  40.     {
  41.         string name = "thread-" + to_string(i);
  42.         Thread thread(name);
  43.         threads.push_back(thread);
  44.         threads[i].start();
  45.       
  46.     }
  47.     for (auto &thread : threads)
  48.     {
  49.         thread.Join();
  50.     }
  51.     return 0;
  52. }
复制代码
代码征象


可以发现,有些_threadname没有打印出来,有些打印出来了,因为加了sleep的原因,这个征象还是比较固定,容易分析的,那么原因出在哪了,没错,就是vector,vector的扩容题目。
分析代码

   线程并发执行引发的vector扩容造成的线程安全题目
  


   分析征象
  


来验证一下vector的扩容

   只必要添加一行,std::cout<<threads.capacity()<<std::endl;
  1. for (int i = 0; i < 10; i++)
  2.     {
  3.         std::cout<<threads.capacity()<<std::endl;
  4.         string name = "thread-" + to_string(i);
  5.         Thread thread(name);
  6.         threads.push_back(thread);
  7.         threads[i].start();
  8.       
  9.     }
复制代码
  征象
  

至此,验证STL容器的vector线程不安全完成。
解决方法

   这里提2种解决办法
1、先把Thread放进vector,最后再遍历vector来启动
2、举行加锁,这里的加锁也要留心,加锁不正确,仍然会导致线程安全题目
  1. int main()
  2. {
  3.     vector<Thread> threads;
  4.     for (int i = 0; i < 10; i++)
  5.     {
  6.         string name = "thread-" + to_string(i);
  7.         Thread thread(name);
  8.         threads.push_back(thread);
  9.     }
  10.     for (int i = 0; i < 10; i++)
  11.     {
  12.         threads[i].start();
  13.     }
  14.     for (auto &thread : threads)
  15.     {
  16.         thread.Join();
  17.     }
  18.     return 0;
  19. }
复制代码
  加锁的代码,注意加锁和释放锁的机遇,不正确的加锁仍然会有题目
  1. #include <iostream>
  2. #include <pthread.h>
  3. #include <string>
  4. #include <vector>
  5. #include <unistd.h>
  6. using namespace std;
  7. pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
  8. class Thread
  9. {
  10. public:
  11.     Thread(string &name) : _threadname(name) {}
  12.     bool start()
  13.     {
  14.         pthread_mutex_lock(&lock);
  15.         // sleep(1); // 这里sleep是为了让现象变得固定,不加sleep,则现象就太随机,不容易画图分析
  16.         int n = pthread_create(&_tid, nullptr, threadroutine, this);
  17.         if (n == 0)
  18.         {
  19.             return true;
  20.         }
  21.         return false;
  22.     }
  23.     static void *threadroutine(void *args)
  24.     {
  25.         
  26.         Thread *self = static_cast<Thread *>(args);
  27.         cout << self->_threadname << endl;
  28.         pthread_mutex_unlock(&lock);
  29.         return nullptr;
  30.     }
  31.     void Join()
  32.     {
  33.         pthread_join(_tid, nullptr);
  34.     }
  35. private:
  36.     pthread_t _tid;
  37.     std::string _threadname;
  38. };
  39. int main()
  40. {
  41.     vector<Thread> threads;
  42.     for (int i = 0; i < 10; i++)
  43.     {
  44.         
  45.         string name = "thread-" + to_string(i);
  46.         Thread thread(name);
  47.         pthread_mutex_lock(&lock);
  48.         threads.push_back(thread);
  49.         pthread_mutex_unlock(&lock);
  50.         threads[i].start();
  51.     }
  52.     for (auto &thread : threads)
  53.     {
  54.         thread.Join();
  55.     }
  56.     return 0;
  57. }
复制代码
小结

在多线程场景中,std::vector 并发访问和扩容会导致数据竞争、无效指针、内存泄漏等题目。通过该案例分析,能够能加深刻的熟悉到线程安全带来的危害,实验的是string,那如果是金钱呢,线程安全题目不可忽视

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4