【问题标题】:C++ multiple threads and vectorsC++ 多线程和向量
【发布时间】:2013-12-02 12:12:52
【问题描述】:

我尝试了以下算法的各种实现,但在程序运行一段时间后总是以崩溃告终...

我有一个基础对象

class Element
    {
    public:
        int a;
        float p;
        Element(int _a, float _p=1.0): a(_a), p(_p){};
    };

我创建了一个向量并包含在一个 Buffer 对象中。

class Buffer
    {
    public:
        Buffer(){};

        vector<Element> raw;       
        vector<Element> optimised; // DATA CALCULATED BASED ON RAW

        void addElement(int _a,float _p=1.0) // FILL THE RAW BUFFER
        {
            raw.push_back(Element(_a,_p));
        }

        void compute()  // COMPUTE THE OPTIMISED BUFFER
        {
            float t;
            int i;
            for(std::vector<Element>::iterator it = raw.begin(); it != raw.end(); ++it) 
            {
                optimised.push_back(Element(it->a,it->p));
                // DO SOME COMPUTATIONALLY INTENSIVE CALCULATIONS
                for(i=1; i<9999999; i++)
                    t = 9./i;
            }
        };

        void clear() // ERASE BOTH BUFFERS
        {
            raw.clear();
            optimised.clear();
        }
    };

我声明了一个 Buffer 对象 - 负责捕获当前数据流 - 和一个 Buffer 对象向量 - 其行为类似于先前创建的缓冲区的历史记录/队列。

Buffer buffer;
vector<Buffer> queue;

主线程负责填充缓冲区对象,并且 - 一旦系列完成 - 将缓冲区提交到队列中。 一旦将新缓冲区添加到队列中,就会在单独的线程上调用 Compute() 函数来分析最近提交的数据。

//ADD THE CURRENT BUFFER TO THE QUEUE
queue.push_back(buffer);

//RUN 'COMPUTE' IN PARALLEL/BACKGROUND ON THE LAST SUBMITTED BUFFER
std::thread t(&Buffer::compute, &queue.back());
t.detach();

//CLEAR THE BUFFER, READY FOR A NEW SERIES
buffer.clear();

程序正常运行并启动,但在执行过程中崩溃(有时只提交一个缓冲区,有时提交几个......如果只有一个缓冲区,它通常“工作更长时间”一次在队列中)。

在这种情况下我需要使用互斥锁吗?如果有,在哪里?

您对如何优化数据收集有任何建议(填充“缓冲区”对象并将其提交到队列中) - 我认为 AddElement() 有点不必要地昂贵?

任何帮助表示赞赏!

谢谢

【问题讨论】:

  • 您似乎没有执行任何同步。为什么不呢?
  • @DavidHeffernan OP 询问这是否有必要。答案是肯定的!
  • 你能给我一个关于我需要做的同步的建议吗?我一直在尝试使用互斥锁,但我仍然遇到了崩溃——除非一次只计算一个缓冲区/线程。我认为我不需要 lock() 因为线程之间没有共享数据(我将缓冲区复制到队列中)。我需要这个算法非常高效和流畅......
  • 将其移入队列并创建另一个?
  • 存储副本时,添加到队列可能会删除列表中的对象并进行复制。所以获取元素的地址是危险的。对容器的任何修改都会使该地址无效。对这些对象执行复制也是非常浪费的。你已经把它们塞满了数据。你不想在不需要的时候复制它。

标签: c++ multithreading vector


【解决方案1】:

问题在于&amp;queue[last]。这为您提供了一个指向向量当前存储缓冲区的位置的指针。如果向量重新分配(push_back 可以这样做),则指针无效。

有几个解决方案:

  • 将指针存储在queue 向量中。 vector&lt;unique_ptr&lt;Buffer&gt;&gt; queue 之类的东西会起作用(并确保您不会意外泄漏内存)。
  • 使用不会使指针在修改时无效的数据结构。 listdeque 可以工作。
  • 确保向量不会重新分配。您可以先发送resize(x),然后自己跟踪最后一次。

更新:添加代码示例。这在 Coliru (http://coliru.stacked-crooked.com/) 上编译并运行良好

#include <memory>
#include <vector>
#include <iostream>
class Buffer {};

int main()
{
    std::unique_ptr<Buffer> buffer {new Buffer()};
    std::vector<std::unique_ptr<Buffer>> queue;
    for (int i = 0; i < 10; i++) {
        buffer.reset(new Buffer());
        // Do things to buffer;
        queue.push_back(move(buffer));
    }
    std::cout << queue.size() << std::endl;
}

【讨论】:

  • 干杯.. 嗯,我如何才能将“缓冲区”(缓冲区类型)添加到“队列”(类型 unique_ptr)?
  • buffer 也必须是一个指针。您可以使用unique_ptr&lt;Buffer&gt; buffer {new Buffer() };,然后使用queue.push_back(move(buffer));
  • @Sorin.. 嗯.. 在你移动了 unique_ptr 之后,你可以重新安装它,然后再做一个新的吗?
  • @mαττjαĸøb 我已经用更完整的示例更新了答案。只要你不接触来自多个线程的相同数据,你就不需要互斥体。
  • 使用 queue.back().get()。这会给你指针,就像你以前一样。 @mαττjαĸøb
猜你喜欢
  • 1970-01-01
  • 2012-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-18
  • 1970-01-01
相关资源
最近更新 更多