当线程数足够大时(即50),堆栈溢出

时间:2018-03-31 22:00:36

标签: c++11 concurrency shared-ptr unique-ptr

我的代码在线程数为15或更少时运行正常,但是当我用更大的线程数运行它(但仍然是一个非常小的数字)时说50。当主函数退出时遇到以下错误,似乎错误发生在清理过程。我无法弄清楚bug的位置。我的开发工具是Visual Studio 2017.这是我的代码:

threadsafe_queue上课:

#pragma once
#include <memory>
#include <mutex>

template<typename T>
class threadsafe_queue
{
private:
    struct Node {
        std::shared_ptr<T> data;
        std::unique_ptr<Node> next;
    };
    Node* tail;
    std::unique_ptr<Node> head;
    std::mutex head_mutex;
    std::mutex tail_mutex;
    std::condition_variable data_cond;

    Node* get_tail();
    std::unique_ptr<Node> pop_head();
    std::unique_lock<std::mutex> wait_for_data();
public:
    threadsafe_queue();
    ~threadsafe_queue();
    threadsafe_queue(const threadsafe_queue& t) = delete;
    threadsafe_queue operator = (const threadsafe_queue& t) = delete;

    void push(T);
    bool try_pop(T&);
    std::shared_ptr<T> try_pop();
    void wait_and_pop(T&);
    std::shared_ptr<T> wait_and_pop();
    bool empty();
};

using namespace std;

template<typename T>
threadsafe_queue<T>::threadsafe_queue() {
    head = std::unique_ptr<Node>(new Node);
    tail = head.get();
}

template<typename T>
threadsafe_queue<T>::~threadsafe_queue()
{
}

template<typename T>
typename threadsafe_queue<T>::Node* threadsafe_queue<T>::get_tail() {
    lock_guard<mutex> lock(tail_mutex);
    return tail;
}

template<typename T>
unique_ptr<typename threadsafe_queue<T>::Node> threadsafe_queue<T>::pop_head()
{
    auto old_head = move(head);
    head = move(old_head->next);
    return old_head;
}

template<typename T>
unique_lock<mutex> threadsafe_queue<T>::wait_for_data()
{
    unique_lock<mutex> headLock(head_mutex);
    data_cond.wait(headLock, [&] {return head.get() != get_tail(); });
    return std::move(headLock);
}

template<typename T>
void threadsafe_queue<T>::wait_and_pop(T & value)
{
    unique_lock<mutex> lock(wait_for_data());
    value = move(pop_head()->data);
}

template<typename T>
shared_ptr<T> threadsafe_queue<T>::wait_and_pop()
{
    unique_lock<mutex> lock(wait_for_data());
    return pop_head()->data;
}

template<typename T>
void threadsafe_queue<T>::push(T newValue)
{
    shared_ptr<T> data(make_shared<T>(std::move(newValue)));
    unique_ptr<Node> new_tail(new Node);
    {
        lock_guard<mutex> lock(tail_mutex);
        tail->data = data;
        Node* new_tail_ptr = new_tail.get();
        tail->next = move(new_tail);
        tail = new_tail_ptr;
    }
    data_cond.notify_one();
}

template<typename T>
bool threadsafe_queue<T>::try_pop(T & value)
{
    lock_guard<mutex> headLock(head_mutex);
    if (head == get_tail())
        return false;
    value = move(pop_head()->data);
    return true;
}

template<typename T>
shared_ptr<T> threadsafe_queue<T>::try_pop()
{
    lock_guard<mutex> headLock(head_mutex);
    if (head == get_tail())
        return shared_ptr<T>();
    return pop_head()->data;
}

template<typename T>
bool threadsafe_queue<T>::empty()
{
    lock_guard<mutex> lock(head_mutex);
    return head.get() == get_tail();
}

main功能:

#pragma once
#include "threadsafe_queue.h"
#include <assert.h>
#include <memory>
#include <atomic>
#include <vector>
#include <thread>

using namespace std;
void worker(threadsafe_queue<int>& queue, std::atomic<int>& count, int const & pushcount, int const & popcount) {
    for (unsigned i = 0; i < pushcount; i++) {
        queue.push(i);
        count++;
    }

    for (unsigned i = 0; i < popcount; i++) {
        queue.wait_and_pop();
        count--;
    }
}

int main() {
    threadsafe_queue<int> queue;
    std::atomic<int> item_count = 0;
    std::vector<thread*> threads;
    unsigned const THREAD_COUNT=50, PUSH_COUT=100, POP_COUNT=50;

    for (unsigned i = 0; i < THREAD_COUNT; i++) {
        threads.push_back(new thread(worker, ref(queue), ref(item_count), ref(PUSH_COUT), ref(POP_COUNT)));
    }

    for (auto thread : threads) {
        thread->join();
    }

    for (auto thread : threads) {
        delete thread;
    }
    assert(item_count == THREAD_COUNT * (PUSH_COUT-POP_COUNT));

    return 0;
}

错误消息:

Unhandled exception at 0x00862899 in Sample.exe: 0xC00000FD: Stack overflow 
(parameters: 0x00000001, 0x00E02FDC). occurred

错误的位置在memory库代码中:

    const pointer& _Myptr() const _NOEXCEPT
    {   // return const reference to pointer
    return (_Mypair._Get_second());
    }

1 个答案:

答案 0 :(得分:0)

答案基于@IgorTandetnik上面的评论。基本上我需要实现~threadsafe_queue来迭代地销毁节点。节点是链接的,因此它们将以递归方式被破坏,这会在队列中剩余的节点数量相对较大时导致堆栈溢出。下面是析构函数代码。

threadsafe_queue<T>::~threadsafe_queue(){
    Node* current = head.release();
    while (current != tail) {
        Node* temp = (current->next).release();
        delete current;
        current = temp;
    }
    delete tail;
}