原子地出发的最简单方法?

时间:2016-07-07 15:14:15

标签: c++ multithreading atomic

我有一组必须使用多线程同时处理的数据,据说数据的数量大于线程的数量。我决定将数据放入某种队列中,这样每个自由线程都可以弹出它的部分并处理它直到队列为空。当我想要从中取出一个元素时,我可以使用一个简单的STL队列并用互斥锁将其锁定,但我想尝试一种无锁方法。与此同时,我的项目太小,无法依赖某些提供无锁结构的第三方库,实际上我只需要原子队列化。所以我决定基于一个带有指向“head”的指针的向量来实现我自己的队列,并以原子方式递增这个指针:

template <typename T>
class AtomicDequeueable
{
public:

    // Assumption: data vector never changes
    AtomicDequeueable(const std::vector<T>& data) :
        m_data(data),
        m_pointer(ATOMIC_VAR_INIT(0))
    {}

    const T * const atomicDequeue()
    {
        if (std::atomic_load(&m_pointer) < m_data.size())
        {
            return &m_data
            [
                std::atomic_fetch_add(&m_pointer, std::size_t(1))
            ];
        }

        return nullptr;
    }

private:

    AtomicDequeueable(const AtomicDequeueable<T>&) {}

    std::atomic_size_t m_pointer;
    const std::vector<T>& m_data;
};

Threads'函数如下所示:

void f(AtomicDequeueable<Data>& queue)
{
    while (auto dataPtr = queue.atomicDequeue())
    {
        const Data& data = *dataPtr;
        // processing data...
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
}

我使用无锁结构和原语的经验非常糟糕,所以我想知道:我的方法会正常工作吗?当然我已经在Ideone上测试了它,但我不知道它对真实数据的表现如何。

2 个答案:

答案 0 :(得分:1)

目前,您的atomicDequeue函数有一个数据竞争:2个线程可能在执行第二个之前执行第一个atomic指令。但是,这可以修复,因为您实际上只需要1个原子操作,按照以下更改:

const T * const atomicDequeue()
{
    auto myIndex = std::atomic_fetch_add(&m_pointer, std::size_t(1));

    if(myIndex >= m_data.size())
        return nullptr;

    return &m_data[myIndex];
}

这项工作在线程操作期间没有修改输入向量。

答案 1 :(得分:-1)

你的代码非常错误。让我在这里的建议中非常坦诚:

  • Actum Ne Agas:不要做已经完成的事情。”你已经很多预先存在的C ++类可供您使用,可以可靠地进行排队,以及进程间通信(IPC)。使用其中一个。

  • 不要担心“无锁”。这就是锁定 for ,它们可靠,快速,便宜。

你的“使用队列”这个概念是合理的,但是你会做出不必要的工作......并且制造错误......当你可以简单地“抓住现成的东西”时。您知道标准部件可以正常工作,因为其他人已将其测试为死亡。