【发布时间】:2021-11-29 12:06:53
【问题描述】:
我有一个被多个线程使用的结构实例。每个线程都包含未知数量的函数调用,这些函数调用会改变结构成员变量。
我有一个专用函数尝试为当前线程“保留”结构实例,我想确保在原始线程允许之前没有其他线程可以保留该实例。
我想到了互斥锁,因为它们可以用来保护资源,但我只知道 std::lock_guard 在单个函数的范围内,但不会为锁定和解锁之间的所有函数调用添加保护。
当我知道它总是按该顺序调用reserve和release时,是否可以保护这样的资源?
更好地解释它的片段:
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
struct information_t {
std::mutex mtx;
int importantValue = 0;
// These should only be callable from the thread that currently holds the mutex
void incrementIt() { importantValue++; }
void decrementIt() { importantValue--; }
void reset() { importantValue = 0; }
} protectedResource; // We only have one instance of this that we need to work with
// Free the resource so other threads can reserve and use it
void release()
{
std::cout << "Result: " << protectedResource.importantValue << '\n';
protectedResource.reset();
protectedResource.mtx.unlock(); // Will this work? Can I guarantee the mtx is locked?
}
// Supposed to make sure no other thread can reserve or use it now anymore!
void reserve()
{
protectedResource.mtx.lock();
}
int main()
{
std::thread threads[3];
threads[0] = std::thread([]
{
reserve();
protectedResource.incrementIt();
protectedResource.incrementIt();
release();
});
threads[1] = std::thread([]
{
reserve();
// do nothing
release();
});
threads[2] = std::thread([]
{
reserve();
protectedResource.decrementIt();
release();
});
for (auto& th : threads) th.join();
return 0;
}
【问题讨论】:
-
一个更好的习惯用法可能是monitor,它可以保持资源的锁定并提供对所有者的访问权限。要获取资源,
reserve()可以返回这样的监视器对象(类似于访问资源内容的代理)。任何对reserve()的竞争访问现在都将被阻止(因为互斥锁已被锁定)。当资源拥有线程完成时,它只会破坏监视器对象,进而解锁资源。 (这允许将 RAII 应用于所有这些,从而使您的代码安全且可维护。) -
“但不要为锁定和解锁之间的所有函数调用添加保护。” -- 这个前提是错误的,或者至少非常具有误导性。
-
我同意。措辞不好。你会如何改写它,所以它更清楚?你明白我想说什么吗?
-
互斥锁不保护对象。它们保护代码不被同时执行。您希望保护以免彼此同时运行的所有代码片段(即您的案例中的所有关键成员函数)都需要锁定相同的互斥锁。如果这些代码可以相互调用,这并不理想,您需要递归互斥锁或另一层成员函数。显示器可能是更好的解决方案。
标签: c++ multithreading