gpt4 book ai didi

c++ - 合并数据和它的锁时不可能是常量正确的?

转载 作者:可可西里 更新时间:2023-11-01 18:28:33 24 4
gpt4 key购买 nike

我一直在寻找将多个线程访问的一段数据与为线程安全提供的锁结合起来的方法。我想我已经到了一个地步,我认为在保持常量正确性的同时不可能做到这一点。

以下面的类为例:

template <typename TType, typename TMutex>
class basic_lockable_type
{

public:
typedef TMutex lock_type;

public:
template <typename... TArgs>
explicit basic_lockable_type(TArgs&&... args)
: TType(std::forward<TArgs...>(args)...) {}

TType& data() { return data_; }
const TType& data() const { return data_; }

void lock() { mutex_.lock(); }
void unlock() { mutex_.unlock(); }

private:
TType data_;
mutable TMutex mutex_;

};

typedef basic_lockable_type<std::vector<int>, std::mutex> vector_with_lock;

在此我尝试结合数据和锁,将mutex_标记为mutable。不幸的是,这还不够,因为在使用时,vector_with_lock 必须标记为 mutable 才能从 执行读取操作const 函数不完全正确(data_ 应该是来自 const 的 mutable)。

void print_values() const
{
std::lock_guard<vector_with_lock> lock(values_);
for(const int val : values_)
{
std::cout << val << std::endl;
}
}

vector_with_lock values_;

无论如何,任何人都可以看到在结合数据和锁的同时保持 const-correctness 吗?另外,我在这里做了什么不正确的假设吗?

最佳答案

就我个人而言,我更喜欢一种您不必手动锁定的设计,并且数据以一种您不先锁定就无法实际访问的方式正确封装。

一个选择是使用友元函数 apply 或执行锁定、抓取封装数据并将其传递给在其中持有锁的情况下运行的函数对象。

//! Applies a function to the contents of a locker_box
/*! Returns the function's result, if any */
template <typename Fun, typename T, typename BasicLockable>
ResultOf<Fun(T&)> apply(Fun&& fun, locker_box<T, BasicLockable>& box) {
std::lock_guard<BasicLockable> lock(box.lock);
return std::forward<Fun>(fun)(box.data);
}
//! Applies a function to the contents of a locker_box
/*! Returns the function's result, if any */
template <typename Fun, typename T, typename BasicLockable>
ResultOf<Fun(T const&)> apply(Fun&& fun, locker_box<T, BasicLockable> const& box) {
std::lock_guard<BasicLockable> lock(box.lock);
return std::forward<Fun>(fun)(box.data);
}

然后用法变为:

void print_values() const
{
apply([](std::vector<int> const& the_vector) {
for(const int val : the_vector) {
std::cout << val << std::endl;
}
}, values_);
}

或者,您可以滥用基于范围的 for 循环来正确确定锁的范围并将值提取为“单个”操作。所需要的只是一组合适的迭代器1:

 for(auto&& the_vector : box.open()) {
// lock is held in this scope
// do our stuff normally
for(const int val : the_vector) {
std::cout << val << std::endl;
}
}

我认为应该有一个解释。一般的想法是 open() 返回一个 RAII 句柄,该句柄在构造时获取锁并在销毁时释放它。基于范围的 for 循环将确保这个临时变量在该循环执行期间一直有效。这提供了正确的锁定范围。

RAII 句柄还为具有单个包含值的范围提供 begin()end() 迭代器。这就是我们获取 protected 数据的方式。基于范围的循环负责为我们解除引用并将其绑定(bind)到循环变量。由于范围是单例,“循环”实际上总是恰好运行一次。

box 不应提供任何其他获取数据的方式,以便它实际上强制执行互锁访问。

当然,一旦盒子打开,就可以隐藏对数据的引用,以一种在盒子关闭后引用可用的方式。但这是为了防止墨菲,而不是马基雅维利。

结构看起来很奇怪,所以我不会责怪任何人不想要它。一方面我想使用它是因为语义是完美的,但另一方面我不想因为这不是基于范围的目的。在握持手上,这种 range-RAII 混合技术相当通用,很容易被滥用到其他目的,但我会把它留给你的想象力/噩梦;)请自行决定使用。


1 留给读者作为练习,但可以在我自己的 locker_box implementation 中找到这样一组迭代器的简短示例。 .

关于c++ - 合并数据和它的锁时不可能是常量正确的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13471628/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com