草庐IT

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

coder 2023-11-17 原文

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

以下面的类为例:

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/

有关c++ - 合并数据和它的锁时不可能是常量正确的?的更多相关文章

  1. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  2. ruby-on-rails - 未初始化的常量 Psych::Syck (NameError) - 2

    在我的gem中,我需要yaml并且在我的本地计算机上运行良好。但是在将我的gem推送到ruby​​gems.org之后,当我尝试使用我的gem时,我收到一条错误消息=>"uninitializedconstantPsych::Syck(NameError)"谁能帮我解决这个问题?附言RubyVersion=>ruby1.9.2,GemVersion=>1.6.2,Bundlerversion=>1.0.15 最佳答案 经过几个小时的研究,我发现=>“YAML使用未维护的Syck库,而Psych使用现代的LibYAML”因此,为了解决

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  5. ruby-on-rails - active_admin 目录中的常量警告重新声明 - 2

    我正在使用active_admin,我在Rails3应用程序的应用程序中有一个目录管理,其中包含模型和页面的声明。时不时地我也有一个类,当那个类有一个常量时,就像这样:classFooBAR="bar"end然后,我在每个必须在我的Rails应用程序中重新加载一些代码的请求中收到此警告:/Users/pupeno/helloworld/app/admin/billing.rb:12:warning:alreadyinitializedconstantBAR知道发生了什么以及如何避免这些警告吗? 最佳答案 在纯Ruby中:classA

  6. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  7. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  8. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

    我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

  9. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  10. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

随机推荐