【问题标题】:Thread safety and `const`线程安全和`const`
【发布时间】:2008-12-12 09:01:49
【问题描述】:

const(指针、引用和成员函数)如何帮助 C++ 中的线程安全?

【问题讨论】:

  • 它没有。仅仅因为对变量的引用是 const,并不意味着没有对该变量的非 const 引用。
  • @ChrisBecke 诚然,这个stackoverflow.com/questions/14127379/… 是一个更好的问题,后见之明。我认为那里的答案表明,油嘴滑舌的“它没有”是不够的。

标签: c++ multithreading


【解决方案1】:

任何不可变(即不可更改)的数据本质上都是线程安全的 - 多个线程同时读取相同的只读数据没有风险,因为它永远不会改变!

在 C++ 中将变量标记为 const 使其只读,因此是线程安全的。

【讨论】:

  • 假设你没有在任何地方使用 mutable ;-)
  • 初始化通常是。为什么要共享尚未完全初始化的对象?
  • 还假设所有成员函数都是可重入的(即没有静态对象或其他共享数据)
  • 我有一些带有错误的并行代码,我发现我正在对成员变量进行看似“无害”的写入,这导致了不严重的内存泄漏。将由多个线程运行的标记方法const 是一个很好的规则!
  • -1,误导...忽略了确保 seq mem 一致性问题。
【解决方案2】:

多线程的主要问题是可变性。 const 限制了这一点,但是由于您可以抛弃 const-ness,因此它并非万无一失。

【讨论】:

  • 没错,但你需要做额外的工作才能在脚上开枪。
  • 我同意,作为一名 C# 程序员,这些天我当然想念 const &
  • 没有代码是万无一失的,但你必须防止墨菲,而不是马基雅维利。
  • 你甚至不需要扔掉它。 const 暗示的唯一一件事是不允许您更改这一点,而不是其他任何人都可以。可以有许多预先存在的对变量的非 const 引用。
【解决方案3】:

const 成员函数不应该改变状态,这样可以安全地同时从多个线程调用。然而线程安全不是 const 的目的,C++ 提供了 mutable 关键字和 const_cast 意味着 const 实际上并不能保证线程安全,因此不应依赖于此目的。

【讨论】:

    【解决方案4】:

    常量函数不是线程安全的。通常,您可以同时从不同的线程调用 const 对象方法,但是如果您从不同的线程调用非 const 和 const 方法,则会出现竞争条件。检查这个:

    class Foo
    {
        size_t size_;
    public:
        ...
        size_t get_size() const
        {
            return size_
        }
    };
    
    class Bar
    {
        boost::shared_ptr<Foo> foo_;
    public:
        //accessor
        size_t get_size() const
        {
            size_t size = 0;
            if (foo_)
                size = foo_->size();
            return size;
        }
        //modifiers
        void init()
        {
            foo_ = new Foo;
        }
    
        void clear()
        {
            foo_ = boost::shared_ptr<Foo>();
        }
    };
    

    如果有人调用init方法,然后同时调用clear和get_size方法,会导致访问冲突。您必须使用读写锁定习语。可以同时调用多个访问器,同时只能调用一个修饰符。 示例:

    class Bar
    {
        boost::shared_ptr<Foo> foo_;
        mutable tbb::spin_rw_mutex lock_;
    public:
        //accessor
        size_t get_size() const
        {
            size_t size = 0;
            //lock modifiers
            rw_mutex_type::scoped_lock lock(mutex, false);
            if (foo_)
                size = foo_->size();
            return size;
        }
        //modifiers
        void init()
        {
            //lock accessor and modifiers
            rw_mutex_type::scoped_lock lock(mutex, true);
            foo_ = new Foo;
        }
    
        void clear()
        {
            //lock accessor and modifiers
            rw_mutex_type::scoped_lock lock(mutex, true);
            foo_ = boost::shared_ptr<Foo>();
        }
    };
    

    tbb::spin_rw_lock 是来自threading builing blocks library 的互斥锁类

    【讨论】:

    • 确实:只有常量帮助构建线程安全的应用程序
    【解决方案5】:

    C++ const 允许非常量别名,例如:

    Foo myVar;
    const Foo* ptr1;
    Foo* ptr2;
    

    鉴于此,const 不保证数据的不变性,即使您不进行任何强制转换或任何绕过它的操作。如果您通过 ptr1 访问 myVar,则无法通过 ptr1 更改它(假设我的语法正确;这就是意图。)但是,它仍然可以通过 ptr2 更改。你真正想要的是一个单独的不可变结构。这在 C++ 中不存在。

    【讨论】:

      【解决方案6】:

      常量和线程安全是正交的概念。

      以 const 函数为例:一个类可能同时具有 const 和 non-const 函数,一个线程可能正在调用一个修改对象的 non-const 函数,同时另一个线程在其 const 函数中。在这种情况下,标记函数 const 不会提供任何安全性。线程安全只能通过锁定或其他同步原语来实现。

      【讨论】:

        猜你喜欢
        • 2016-09-05
        • 2013-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-16
        • 1970-01-01
        • 1970-01-01
        • 2011-06-09
        相关资源
        最近更新 更多