我有简单的代码:第一个线程将 std::strings
推送到 std::list
,第二个线程弹出 std::strings
从这个 std::list
。所有 std::list
的操作都受到 std::mutex m
的保护。此代码将错误永久打印到控制台:"Error: lst.begin() == lst.end()"
。
如果我将 std::lock_guard
替换为构造 m.lock()
和 m.unlock()
代码将开始正常工作。 std::lock_guard
有什么问题?
#include <iostream>
#include <thread>
#include <mutex>
#include <list>
#include <string>
std::mutex m;
std::list<std::string> lst;
void f2()
{
for (int i = 0; i < 5000; ++i)
{
std::lock_guard<std::mutex> { m };
lst.push_back(std::to_string(i));
}
m.lock();
lst.push_back("-1"); // last list's element
m.unlock();
}
void f1()
{
std::string str;
while (true)
{
m.lock();
if (!lst.empty())
{
if (lst.begin() == lst.end())
{
std::cerr << "Error: lst.begin() == lst.end()" << std::endl;
}
str = lst.front();
lst.pop_front();
m.unlock();
if (str == "-1")
{
break;
}
}
else
{
m.unlock();
std::this_thread::yield();
}
}
}
// tested in MSVS2017
int main()
{
std::thread tf2{ f2 };
f1();
tf2.join();
}
最佳答案
你没有服从CppCoreGuidelines CP.44: Remember to name your lock_guards and unique_locks :).
在
for (int i = 0; i < 5000; ++i)
{
std::lock_guard<std::mutex> { m };
lst.push_back(std::to_string(i));
}
您只是在创建一个临时的 std::lock_guard
对象,该对象会立即创建和销毁。您需要像中那样命名对象
{
std::lock_guard<std::mutex> lg{ m };
lst.push_back(std::to_string(i));
}
这样锁守卫就会一直存在到 block 的末尾。
正如您已经认识到的 ( CppCoreGuidelines ):
Use RAII lock guards (lock_guard, unique_lock, shared_lock), never call mutex.lock and mutex.unlock directly (RAII)
如果您使用的是 Microsoft Visual Studio,我建议您使用代码分析并至少激活 Microsoft Native Recommended Rules。如果这样做,您将收到编译器分析警告。
warning C26441: Guard objects must be named (cp.44).
关于c++ - std::lock_guard 有什么问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53986760/