消费者线程中的函数无法访问内存位置

Function in consumer thread unable to access memory location

我有一些处理图像的代码。性能至关重要,所以我很想使用 BoundedBuffer 来实现多线程。图像数据存储为 unsigned char*(由我用来处理图像数据的 SDK 指定)。

问题出现在消费者线程中调用的 processData 函数中。在 processData 中,还有另一个函数(来自图像处理 SDK)使用了 cudaMemcpy2D 函数。 cuda函数总是抛出一个异常说访问冲突读取位置。

但是,如果我直接在生产者线程或 deposit 中调用 processData,则 cuda 函数工作正常。当我从消费者线程调用 processData 时(根据需要),我从 cuda 函数中得到了异常。我什至尝试从 fetch 调用 processData,但我遇到了同样的异常。

我的猜测是,在生产者线程将数据 deposit 编辑到 rawImageBuffer 之后,unsigned char* 指向的内存不知何故发生了变化,因此消费者线程(或fetch) 实际上将错误的图像数据发送到 processData(和 cuda 函数)。

这是我的代码的样子:

void processData(vector<unsigned char*> unProcessedData)
{
    // Process the data
}

struct BoundedBuffer {
    queue<vector<unsigned char*>> buffer;
    int capacity;

    std::mutex lock;

    std::condition_variable not_full;
    std::condition_variable not_empty;

    BoundedBuffer(int capacity) : capacity(capacity) {}

    void deposit(vector<unsigned char*> vData) 
    {
        std::unique_lock<std::mutex> l(lock);

        bool bWait = not_full.wait_for(l, 3000ms, [this] {return buffer.size() != capacity; }); // Wait if full

        if (bWait)
        {
            buffer.push(vData); // only push data when timeout doesn't expire
            not_empty.notify_one();
        }           
    }

    vector<unsigned char*> fetch()
    {
        std::unique_lock<std::mutex> l(lock);

        not_empty.wait(l, [this]() {return buffer.size() != 0; }); // Wait if empty

        vector<unsigned char*> result{};

        result = buffer.front();
        buffer.pop();

        not_full.notify_one();

        return result;
    }
};

void producerTask(BoundedBuffer &rawImageBuffer)
{
    for(;;)
    {
        // Produce Data
        vector<unsigned char*> producedDataVec{dataElement0, dataElement1};
        rawImageBuffer.deposit(producedDataVec);
    } //loop breaks upon user interception
}

void consumerTask(BoundedBuffer &rawImageBuffer)
{
    for(;;)
    {
        vector<unsigned char*> fetchedDataVec{};
        fetchedDataVec = rawImageBuffer.fetch();
        processData(fetchedDataVec);
    } //loop breaks upon user interception 
}

int main()
{
        BoundedBuffer rawImageBuffer(6);

        thread consumer(consumerTask, ref(rawImageBuffer));
        thread producer(producerTask, ref(rawImageBuffer), 

        consumer.join();
        producer.join();

        return 0;
}

我对抛出异常的原因的猜测是否正确?我该如何解决这个问题?作为参考,每个向量元素都包含 RGBa 8 位格式的 2448px X 2048px 图像的数据。

更新:

  1. 在评论中有人指出unsigned char*指针可能无效后,我发现指针指向的地址实际上是一个真正的内存位置。异常中访问违规读取位置 X。X大于指针指向的位置。

  2. 经过调试,发现unsigned char* in unprocessedData vector in processData指向的内存不完整,指针地址正确,但有些内存块不可读。我通过在 processData 中的 unsigned char* 中打印每个 char 找到了这个。当生产者线程调用 processData 时(这是 cuda 不抛出异常的时候),所有 chars 都打印得很好(我正在打印 2048*2448*4 chars,由上述图像分辨率和格式决定)。但是当消费者线程调用 processData 时,打印 char 会抛出相同的异常,异常会在第 40 个 char 左右抛出(大约第 40 个,并不总是第 40 个)。

  3. 好的,所以现在我很确定不仅我的指针指向真实的内存位置,我还知道指针指向的第一个内存块多次保持预期值正如我测试过的那样。为了测试这个,在producerTask中我特意写了一个测试值(比如int42,或者char*)到[=12指向的第0内存块=].在 processData 函数中,我检查内存块是否仍然包含测试值,它确实包含。所以,现在我知道指针指向的一些内存块由于某些未知原因变得无法读取。另外,我的测试并不能证明第一个内存块不会变得不可访问,只是它在我进行的几次测试中并没有变得不可访问。 更新 1 到 3 的 TLDRunprocessedImage 指针是有效的,它们指向一个真实的内存地址,它们也指向保存预期值的内存地址。

  4. 又一次调试尝试。现在我正在使用 Visual Studio 的内存 window 来目视检查数据。调试器告诉我 unProcessedData[0] 指向 0x00000279d7c760700x00000279d7c76070 周围的内存是这样的: 内存似乎是合理的,可以清楚地看到 RGBa 格式,图像全黑所以 RGB 通道接近 0 而 alphaff 是有道理的。我向下滚动了很长时间以查看内存的样子,一直到 0x00000279D8F9606F 数据看起来不错(符合预期的 RGBa 值)。 0x00000279D8F9606F 数字也有意义,因为 0x00000279D8F9606F - 0x00000279d7c76070 = 0d20054015,这意味着有 20054016 个有效的 char 是预期的(2048 高度 * 2448 宽度) *4 个频道 = 20054016)。好的,到目前为止一切顺利。请注意,所有这些都在 运行 cuda 函数之前。在单步执行 cuda 函数后,我得到了相同的异常:访问违规读取位置 0x00000279D80B8000。请注意 0x00000279D80B80000x00000279d7c760700x00000279D8F9606F 之间,我目视检查的内存部分是正确的。现在,在 运行 之后,这里的 cuda 函数是 0x00000279d7c760700x00000279D8F9606F 之间的内存:

  5. 当我 cout 在调用 cuda 函数之前 processData 中的任何内容。指针指向的内存发生变化。如下图所示,所有 char 都等同于 0xddThis MSDN 上的页面说 The freed blocks kept unused in the debug heap's linked list when the _CRTDBG_DELAY_FREE_MEM_DF flag is set are currently filled with 0xDD. 但是当我从生产者线程调用 processData 时,指向的内存在我 cout 之后没有改变。

目前对这个问题投票最多的评论是告诉我学习更多关于指针的知识。我目前正在这样做(希望正如我的更新所暗示的那样),但是我需要了解哪些主题?我知道指针是如何工作的。我知道我的指针指向有效的内存位置(请参阅更新 2)。我知道指针指向的一些内存块变得无法读取(参见更新 3)。但我不知道为什么内存块变得不可访问。特别是,我不知道为什么只有当从消费者线程调用 processData 时它们才变得不可访问(请注意,当从生产者线程调用 processData 时没有抛出异常)。我还能做些什么来帮助缩小这个问题的范围吗?

问题相当简单,n.m。的评论引导我朝着正确的方向前进,我对此表示感谢。

在我的更新中,我提到使用 cout 打印任何内容都会导致数据损坏。虽然,这似乎正在发生,但在 fetchdeposit 中放置一些断点后,我对真正发生的事情有了一个完整的了解。

我生成图像数据的方式是使用相机随附的另一个SDK,SDK 以wrapped pointer 的类型向我提供图像数据。然后我转换了图像格式,然后展开转换后的图像以获得指向原始图像的指针。然后将指向原始图像的指针存储到 producedDataVec 中,并将其 deposit 编辑到 rawImageBuffer 中。问题是一旦转换后的图像超出范围,我的数据就会损坏。因此,cout 语句并不是真正导致我的数据损坏的原因。断点无处不在,我可以看到数据在转换后的图像超出范围后立即损坏。为了解决这个问题,现在我的生产者直接 deposits 指向缓冲区的包装指针。 consumer fetches wrapped pointer,在consumer中转换格式得到转换后的图片,再得到raw image指针。现在转换后的图像仅在 processData 返回后超出范围,因此永远不会抛出异常。