8

假设我有一个看起来像这样的类(实际上正是这个大小):

class K
{
public:

    long long get_x() const; // lock m_mutex in shared/read-only mode

    void update( long long w ); // lock m_mutex with a unique_lock

private:
    long long m_a;
    long long m_b;
    long long m_c;
    long long m_x;
    double m_flow_factor;

    mutable boost::shared_mutex m_mutex;
};

如您所见,这应该是线程安全的。更新函数一次被一个线程调用,未知但只有一个线程(保证),但是访问器可以被多个线程同时调用。

更新函数正在改变所有的值并且被非常频繁地调用(每秒一百次)。您可以猜到,当前的实现将锁定很多。

我正在考虑使用 std::atomic 来避免锁定并可能使此代码更有效。但是,我真的需要更新函数来一起更新值。因此,我正在考虑做这样的事情:

class K
{
public:

    long long get_x() const
    { return data.load().x; }

    void update( long long w )
    {
        auto data_now = data.load();
        // ... work with data_now
        data.store( data_now );
    }

private:
    struct Data {
    long long a;
    long long b;
    long long c;
    long long x;
    double flow_factor;
    };
    std::atomic<Data> data;
};

我目前对 std::atomic 的理解是,即使这段代码比前一个代码更具可读性(因为它没有到处都有锁声明),因为 K::Data 结构是“大”的,std::atomic将只使用普通的互斥锁来实现(所以它不应该比我的初始实现更快)。

我对么?

4

3 回答 3

12

像这样的结构的 std:atomic 的任何特化都将涉及内部锁定,所以你什么也没得到,现在你在加载和存储之间也有你以前没有的数据竞争,因为它有排他锁定在以前的版本中围绕整个街区(我想?)。

同样对于 shared_mutex,使用普通互斥锁与 shared_mutex 进行分析可能是明智的,您可能会发现普通互斥锁执行得更好(这一切都取决于您持有锁的时间)。

shared_mutex 的好处只有在锁被长时间持有以供读取并且写入很少时才能看到,否则 shared_mutex 所涉及的开销会扼杀您对普通互斥锁的任何收益。

于 2012-12-23T05:33:27.557 回答
1

std::atomic 不一定比 std::mutex 慢。例如在 MSVC 14.0 中,std::atomic.store 的实现如下所示:

inline void _Atomic_copy(
volatile _Atomic_flag_t *_Flag, size_t _Size,
    volatile void *_Tgt, volatile const void *_Src,
        memory_order _Order)
{   /* atomically copy *_Src to *_Tgt with memory ordering */
_Lock_spin_lock(_Flag);
_CSTD memcpy((void *)_Tgt, (void *)_Src, _Size);
_Unlock_spin_lock(_Flag);
}

inline void _Lock_spin_lock(
volatile _Atomic_flag_t *_Flag)
{   /* spin until _Flag successfully set */
while (_ATOMIC_FLAG_TEST_AND_SET(_Flag, memory_order_acquire))
    _YIELD_PROCESSOR;
}

不能保证自旋锁会比正确的 std::mutex 更快。这取决于你到底在做什么。但与 std::mutex 相比,std::atomic 肯定不总是一个次优的解决方案。

于 2017-09-21T06:34:23.240 回答
1

无论是否使用锁实现,您对 atomic 的默认使用(即具有顺序一致性)都非常慢,并且可能与互斥锁一样慢,如果不慢的话。调整内存屏障update()可能会带来更好的性能:

void update( long long w )
{
    auto data_now = data.load(std::memory_order_acquire);
    // ... work with data_now
    data.store(data_now, std::memory_order_release);
}

此外,如果您不需要线程之间的顺序一致性(您不同步写入器与读取器),您可以进一步优化读取器:

long long get_x() const
{ return data.load(std::memory_order_relaxed).x; }
于 2020-05-15T11:29:07.480 回答