编辑:看来,问题是我没有真正创建一个lock_guard的本地实例,而只是一个匿名临时的,它立即被再次销毁,正如评论指出的那样下面。std :: lock_guard导致未定义的行为
编辑2:启用clang的线程清理程序有助于在运行时查明这些类型的问题。它可以通过
clang++ -std=c++14 -stdlib=libc++ -fsanitize=thread *.cpp -pthread
启用这可能在某种程度上是一个重复的问题,但我无法找到任何东西,所以如果真的是重复我很抱歉。无论如何,这应该是一个初学者的问题。
我用一个简单的“计数器”类玩弄,内联说,在文件
Counter.hpp:
#ifndef CLASS_COUNTER_HPP_
#define CLASS_COUNTER_HPP_
#include <mutex>
#include <string>
#include <exception>
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<std::mutex>{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中的计数器实例应该是递增和递减 同时:
main.cpp中:
#include <iostream>
#include <iomanip>
#include <array>
#include <thread>
#include <exception>
#include "Counter.hpp"
int
main()
{
Counter counter{};
std::array<std::thread,4> 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的问题。如果我定义减量()函数,因为这:
void decrement() noexcept
{
mutex_.lock();
--value_; // possible underflow
mutex_.unlock();
}
一切顺利罚款(只要没有下溢)。 但是,一旦我做一个简单的改变:
void decrement() noexcept
{
std::lock_guard<std::mutex>{mutex_};
--value_; // possible underflow
}
的行为就像我上面描述。我认为我并不真正了解std :: lock_guard的行为和用例。如果您能指引我走向正确的方向,我将不胜感激!
程序通过clang++ -std=c++14 -stdlib=libc++ *.cpp -pthread
编译。
@DarkFalcon这是意图抛出一个异常,如果客户端已经是0,那么客户端会尝试减小该值。以类的实例初始化的方式,它只能是0或更大(size_t应该是无符号的无论如何),所以如果它是0,它应该保持这种方式。这个问题不是真的想要实现一个完美的计数器,而是关于如何理解std :: unique_lock的工作原理。 – mbw
如果不是锁定互斥量,而是使用原子增量/减量,则可能会获得更好的性能。 – SergeyA