【问题标题】:Stack overflow when thread number is large enough (i.e, 50)当线程数足够大(即 50)时堆栈溢出
【发布时间】:2018-09-10 13:57:51
【问题描述】:

当线程号为 15 或更少时,我的代码运行正常,但是当我使用更大的线程号(但仍然是一个非常小的数字)运行它时,说 50。当主函数退出时,我遇到了以下错误,似乎错误发生在清理过程。我无法弄清楚错误在哪里。我的开发工具是 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());
    }

【问题讨论】:

  • ~Node 递归调用自身(通过销毁next,后者在下一个Node 上调用析构函数,依此类推),递归深度与队列长度一样长。当队列在销毁时足够长时,这将导致堆栈溢出。让~threadsafe_queue 遍历列表并迭代地销毁每个节点。
  • @IgorTandetnik 啊哈,因为我没有从队列中弹出所有内容,所以我还有剩余的节点,所以剩余的项目在最后被销毁,这种情况我需要在我的 ~threadsafe_queue 中处理。谢谢!

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


【解决方案1】:

答案基于上面@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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-07
    • 2015-04-23
    • 2016-05-07
    • 2021-05-26
    • 2015-09-08
    • 2013-11-05
    • 2013-07-11
    相关资源
    最近更新 更多