加入时线程的C ++向量随机崩溃

时间:2019-06-10 10:38:06

标签: c++ multithreading

我正在使用线程向量进行繁重的工作,之后我将它们称为join。有时,一切正常,并且可以按预期加入它们。但是,在某些情况下,我感到不由自主地看起来有些混乱,他们崩溃了,说向量迭代器来自另一个容器。

这是我执行多线程处理的功能。

int FindPath(const int nStartX, const int nStartY,
    const int nTargetX, const int nTargetY,
    const unsigned char* pMap, const int nMapWidth, const int nMapHeight,
    int* pOutBuffer, const int nOutBufferSize)
{
    vector<Node> nodes(nMapWidth * nMapHeight);
    priority_queue<Node*, vector<Node*>, Compare> queue;
    vector<thread> threads;

    getNodes(nodes, nStartX, nStartY, nTargetX, nTargetY, pMap, nMapWidth, nMapHeight);

    queue.push(&nodes[getCoord(nMapWidth, nStartX, nStartY)]);

    for (auto i = 0; i < thread::hardware_concurrency(); ++i)
    {
        threads.push_back(thread(doWork, ref(queue)));
    }
    for (auto& worker : threads)
    {
        worker.join();
    }

    if (nodes[getCoord(nMapWidth, nTargetX, nTargetY)].prev)
    {
        vector<int> path;

        getPath(path, nodes[getCoord(nMapWidth, nTargetX, nTargetY)]);

        for (auto i = 0; i < nOutBufferSize; ++i)
        {
            if (i >= path.size())
            {
                break;
            }
            else
            {
                pOutBuffer[i] = path[i];
            }
        }

        return path.size();
    }
    else
    {
        return -1;
    }
}

尤其是这部分是随机发生崩溃的地方。

for (auto& worker : threads)
{
    worker.join();
}
void doWork(priority_queue<Node*, vector<Node*>, Compare>& queue)
{
    while (true)
    {
        if (!queue.size())
        {
            unique_lock<mutex> ml(mtx);
            cv.wait_until(ml, chrono::system_clock::now() + 10ms);

            if (!queue.size())
            {
                break;
            }
        }
        else
        {
            Node* node = queue.top();
            queue.pop();

            for (auto neighb : node->neighb)
            {
                if (node->distPrev + neighb.second < neighb.first->distPrev)
                {
                    neighb.first->distPrev = node->distPrev + neighb.second;
                    neighb.first->prev = node;

                    queue.push(neighb.first);

                    cv.notify_one();
                }
            }
        }
    }
}

如果有帮助,我正在使用VSS 2019社区版本。

2 个答案:

答案 0 :(得分:2)

您需要围绕队列的所有读写进行同步。

这样的事情(出于明显的原因,未经测试):

void doWork(priority_queue<Node*, vector<Node*>, Compare>& queue)
{
    while (true)
    {
        Node* node = nullptr;
        {
            // Wait for an item to appear, or 10 ms to pass.
            unique_lock<mutex> ml(mtx);
            if (queue.empty())
            {
                // If the queue is still empty after 10ms, break out.
                if (!cv.wait_for(ml, 10ms, [&queue]() { return !queue.empty(); }))
                    break;
            }
            // The queue can't be empty here.
            node = queue.top();
            queue.pop();
        }

        // Add neighbours.
        for (auto neighb : node->neighb)
        {
            if (node->distPrev + neighb.second < neighb.first->distPrev)
            {
                neighb.first->distPrev = node->distPrev + neighb.second;
                neighb.first->prev = node;
                // Lock while adding to the queue.
                unique_lock<mutex> ml(mtx);
                queue.push(neighb.first);
                cv.notify_one();
            }
        }
    }
}

请注意,“等待十毫秒后队列为空”并不是确定工作已完成的非常可靠的方法。

或分为两个功能:

Node* fetch_next(priority_queue<Node*, vector<Node*>, Compare>& queue)
{
    unique_lock<mutex> ml(mtx);
    if (queue.empty())
    {
        if (!cv.wait_for(ml, 10ms, [&queue]() { return !queue.empty(); }))
            return nullptr;
    }
    Node* node = queue.top();
    queue.pop();
    return node;
}

void doWork(priority_queue<Node*, vector<Node*>, Compare>& queue)
{
    while (Node* node = fetch_next(queue))
    {
        for (auto neighb : node->neighb)
        {
            if (node->distPrev + neighb.second < neighb.first->distPrev)
            {
                neighb.first->distPrev = node->distPrev + neighb.second;
                neighb.first->prev = node;
                unique_lock<mutex> ml(mtx);
                queue.push(neighb.first);
                cv.notify_one();
            }
        }
    }
}

答案 1 :(得分:0)

我的猜测是:dowork()中的以下代码被多个线程异步执行,因此产生了问题:

if (!queue.size()) 

在else块中,按以下方式访问队列,再次由多个线程异步访问:

Node* node = queue.top();
queue.pop();

同时保护互斥对象下的上述代码片段可能是一个更好的主意?