当前位置:  开发笔记 > 编程语言 > 正文

std :: lock_guard导致未定义的行为

如何解决《std::lock_guard导致未定义的行为》经验,为你挑选了2个好方法。

编辑:看起来,问题是我实际上并没有创建一个lock_guard的本地实例,而只是一个匿名临时实例,它立即被再次销毁,正如下面的评论所指出的那样.

Edit2:启用clang的线程清理程序有助于在运行时查明这些类型的问题.它可以通过启用

clang++ -std=c++14 -stdlib=libc++ -fsanitize=thread *.cpp -pthread

这可能在某种程度上是一个重复的问题,但我找不到任何东西,所以如果它真的是重复的我很抱歉.无论如何,这应该是一个初学者的问题.

我正在玩一个简单的"计数器"类,在文件中说内联

Counter.hpp:

#ifndef CLASS_COUNTER_HPP_
#define CLASS_COUNTER_HPP_

#include 
#include 
#include 

class Counter
{
     public:
          explicit Counter(std::size_t v = 0) : value_{v} {}

          std::size_t value() const noexcept { return value_; }

//          void increment() { ++value_; }        // not an atomic operation : ++value_ equals value_ = value_ + 1
                                                // --> 3 operations: read, add, assign
          void increment() noexcept
          {
               mutex_.lock();
               ++value_;
               mutex_.unlock();
          }

//          void decrement() noexcept
//          {
//               mutex_.lock();
//               --value_;                      // possible underflow
//               mutex_.unlock();
//          }

          void decrement()
          {
               std::lock_guard{mutex_};
               if (value_ == 0)
               {
                    std::string message{"New Value ("+std::to_string(value_-1)+") too low, must be at least 0"};
                    throw std::logic_error{message};
               }
               --value_;
          }

     private:
          std::size_t value_;
          std::mutex mutex_;
};

#endif

在main.cpp中,Counter实例应该同时递增和递减:

main.cpp中:

#include 
#include 
#include 
#include 
#include 

#include "Counter.hpp"

     int
main ()
{
     Counter counter{};
     std::array threads;
     auto operation = [&counter]()
     {
          for (std::size_t i = 0; i < 125; ++i)
               counter.increment();
     };
//     std::for_each(begin(threads),end(threads),[&operation](auto& val) { val = std::thread{operation}; });
     std::cout << "Incrementing Counter (" << std::setw(3) << counter.value() << ") concurrently...";
     for (auto& t : threads)
     {
          t = std::thread{operation};
     }

     for (auto& t : threads)
          t.join();
     std::cout << " new value == " << counter.value() << '\n';

     auto second_operation = [&counter]()
     {
          for (std::size_t i = 0; i < 125; ++i)
          {
               try
               {
                    counter.decrement();
               }
               catch(const std::exception& e)
               {
                    std::cerr << "\n***Exception while trying to decrement : " << e.what() << "***\n";
               }
          }
     };

     std::cout << "Decrementing Counter (" << std::setw(3) << counter.value() << ") concurrently...";
     for (auto& t : threads)
          t = std::thread{second_operation};
     for (auto& t : threads)
          t.join();
     std::cout << " new value == " << counter.value() << '\n';

     return 0;

异常处理似乎按照预期的方式工作,并且我理解它的方式std :: lock_guard应该保证一旦lock_guard超出范围就解锁一个互斥锁.

然而,似乎比这更复杂.虽然增量正确地导致最终值为"500",但是减量 - 应该导致"0" - 不能解决.结果将介于"0"和"16"之间.

如果更改时间,例如使用valgrind,它似乎每次都正常工作.

我能够找到使用std :: lock_guard的问题.如果我将decrement()函数定义为:

      void decrement() noexcept
      {
           mutex_.lock();
           --value_;                      // possible underflow
           mutex_.unlock();
      }

一切都很好(只要没有下溢).但是,一旦我做了一个简单的改变:

      void decrement() noexcept
      {    
           std::lock_guard{mutex_};
           --value_;                      // possible underflow
      }

行为就像我上面描述的那样.我认为我并不真正了解std :: lock_guard的行为和用例.如果你能指出我正确的方向,我将非常感激!

该程序编译通过clang++ -std=c++14 -stdlib=libc++ *.cpp -pthread.



1> Dark Falcon..:

std::lock_guard{mutex_};不创建本地.它创建了一个临时语句,在语句结束时被销毁.这意味着您的值不受锁的保护.锁定装置必须是本地的:

void decrement() noexcept
{    
   std::lock_guard guard {mutex_};
   --value_;                      // possible underflow
}



2> mindriot..:

问题是这条线

std::lock_guard{mutex_};

不创建变量,而是创建一个lock_guard立即再次销毁的临时对象.你可能想写的是:

std::lock_guard guard{mutex_};

这会创建一个lock_guard名为的变量,guard当它离开作用域时会被销毁(即在函数的末尾.实际上,你忘了为你的变量命名.

推荐阅读
小色米虫_524
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有