删除深层嵌套链接结构的引擎

时间:2016-04-16 04:46:57

标签: c++ multithreading performance c++11

背景是Nested shared_ptr destruction causes stack overflow的问题。总而言之,对于看起来像这样的数据结构:

// A node in a singly linked list.
struct Node {
    int head;
    std::shared_ptr<Node> tail;
};

如果列表增长太长,由于Node的递归破坏,shared_ptr的析构函数中可能会发生堆栈溢出。我正在实现https://stackoverflow.com/a/36635668/3234803中提出的删除引擎的线程安全版本。

涉及三个简单的课程:SpinLockConcurrentQueueDeleteEngine

SpinLock类使用std::atomic_flag

实现C ++ Lockable接口
class SpinLock {
public:
    void lock()
    { while (lock_.test_and_set(std::memory_order_acquire) {} }

    bool try_lock() // Not camel-case to be compatible with C++ Lockable interface
    { return !lock_.test_and_set(std::memory_order_acquire); }

    void unlock()
    { lock_.clear(std::memory_order_release); }

private:
    std::atomic_flag lock_ = ATOMIC_FLAG_INIT;
};

ConcurrentQueue类实现了一个支持线程安全的enqueuetryDequeue的多生产者单用户队列:

template<typename T>
class ConcurrentQueue {
public:
    void enqueue(T item)
    {
        std::lock_guard<SpinLock> lk(lock_);
        queue_.emplace_back(std::move(item));
    }

    bool tryDequeue(T &item)
    {
        std::lock_guard<SpinLock> lk(lock_);
        if (queue_.empty()) {
            return false;
        }
        item = std::move(queue_.front());
        queue_.pop_front();
        return true;
    }

private:
    std::deque<T> queue_;
    SpinLock lock_;
};

DeleteEngine类实现上述答案中提出的删除引擎:

template<typename T>
class DeleteEngine {
public:
    ~DeleteEngine()
    {
        std::lock_guard<SpinLock> lk(deleting_);
        deleteAll();
    }

    void enqueue(T *p)
    {
        queue_.enqueue(p);
        if (deleting_.try_lock()) {
            std::lock_guard<SpinLock> lk(deleting_, std::adopt_lock);
            deleteAll();
        }
    }

private:
    void deleteAll()
    {
        T *p = nullptr;
        while (queue_.tryDequeue(p)) {
            delete p;
        }
    }

    ConcurrentQueue<T *> queue_;
    SpinLock deleting_;
};

现在在shared_ptr<Node>的删除中,我们会将原始指针移交给DeleteEngine<Node>,而不是直接delete。递归delete最多可以处理大约10000个节点,而此方法可以处理任意数量的节点。

上面的代码只是一个原型,但我特别关注这个实现的性能:(1)大多数时候Node类将在单线程环境中使用; (2)偶尔它可能用于高度并发的应用程序,例如一个不断创建和销毁对象的Web服务器。

这种实现明显慢于天真的递归破坏(64个线程的微基准测试和每个线程10000个节点显示大约10倍的减速)。我该怎么做才能改善它?

0 个答案:

没有答案