【问题标题】:Mandelbrot image generator in C++ using multi-threading "overwrites(?)" half the imageC++ 中的 Mandelbrot 图像生成器使用多线程“覆盖(?)”一半图像
【发布时间】:2019-04-22 04:55:31
【问题描述】:

我真的不知道如何正确解释我的问题,但希望您可以使用我提供的图像来理解它。

我使用模板和互联网上的教程制作了这个 Mandelbrot 图像生成器,我正在尝试使生成过程多线程,以便图像被分成 4 个相等的部分,每个线程计算该部分。问题是,图像的前半部分变成黑色,而后半部分生成良好。我不知道问题是什么。

这段代码在没有多线程的情况下也能正常工作,所以即使我觉得问题出在那儿我仍然找不到它。

代码:

// mandelbrot.cpp
// compile with: g++ -std=c++11 -pthread mandelbrot.cpp -o mandelbrot
// view output with: eog mandelbrot.ppm

#include <fstream>
#include <iostream>
#include <complex> // if you make use of complex number facilities in C++
#include <thread>
#include <vector>

template <class T>
struct RGB
{
    T r, g, b;
};

template <class T>
class Matrix
{
  public:
    Matrix(const size_t rows, const size_t cols) : _rows(rows), _cols(cols)
    {
        _matrix = new T *[rows];
        for (size_t i = 0; i < rows; ++i)
        {
            _matrix[i] = new T[cols];
        }
    }
    Matrix(const Matrix &m) : _rows(m._rows), _cols(m._cols)
    {
        _matrix = new T *[m._rows];
        for (size_t i = 0; i < m._rows; ++i)
        {
            _matrix[i] = new T[m._cols];
            for (size_t j = 0; j < m._cols; ++j)
            {
                _matrix[i][j] = m._matrix[i][j];
            }
        }
    }
    ~Matrix()
    {
        for (size_t i = 0; i < _rows; ++i)
        {
            delete[] _matrix[i];
        }
        delete[] _matrix;
    }
    T *operator[](const size_t nIndex)
    {
        return _matrix[nIndex];
    }
    size_t width() const { return _cols; }
    size_t height() const { return _rows; }

  protected:
    size_t _rows, _cols;
    T **_matrix;
};

// Portable PixMap image
class PPMImage : public Matrix<RGB<unsigned char>>
{
  public:
    PPMImage(const size_t height, const size_t width) : Matrix(height, width) {}
    void save(const std::string &filename)
    {
        std::ofstream out(filename, std::ios_base::binary);
        out << "P6" << std::endl
            << _cols << " " << _rows << std::endl
            << 255 << std::endl;
        for (size_t y = 0; y < _rows; y++)
            for (size_t x = 0; x < _cols; x++)
                out << _matrix[y][x].r << _matrix[y][x].g << _matrix[y][x].b;
    }
};

const void calculateImage(PPMImage &image, unsigned &width, unsigned &height, unsigned &minX, unsigned &maxX, unsigned &minY, unsigned &maxY)
{
    double MinRe = -2.0;
    double MaxRe = 1.0;
    double MinIm = -1.2;
    double MaxIm = MinIm + (MaxRe - MinRe) * width / height;
    double Re_facor = (MaxRe - MinRe) / (width - 1);
    double Im_factor = (MaxIm - MinIm) / (height - 1);
    unsigned MaxIterations = 30;

    for (unsigned y = minY; y < maxY; ++y)
    {
        double c_im = MaxIm - y * Im_factor;
        for (unsigned x = minX; x < maxX; ++x)
        {
            double c_re = MinRe + x * Re_facor;

            double Z_re = c_re, Z_im = c_im;
            bool isInside = true;
            for (unsigned n = 0; n < MaxIterations; ++n)
            {
                double Z_re2 = Z_re * Z_re, Z_im2 = Z_im * Z_im;
                if (Z_re2 + Z_im2 > 4)
                {
                    isInside = false;
                    break;
                }
                Z_im = 2 * Z_re * Z_im + c_im;
                Z_re = Z_re2 - Z_im2 + c_re;
            }
            if (isInside)
            {
                image[y][x].r = 255;
                image[y][x].g = 0;
                image[y][x].b = 0;
            }
        }
    }
}

int main()
{
    unsigned width = 1600;
    unsigned height = 1600;
    unsigned minX = 0;
    unsigned minY = 0;
    unsigned maxX = 800;
    unsigned maxY = 800;

    std::cout << "Creating image...\n";
    PPMImage image(height, width);

    std::vector<std::thread> workers;
    for (int i = 0; i < 4; i++)
    {
        workers.push_back(std::thread(calculateImage, std::ref(image), std::ref(width), std::ref(height), std::ref(minX), std::ref(maxX), std::ref(minY), std::ref(maxY)));
        std::cout << "Thread #" << i << " created. Calculating (" << minX << "," << minY << ") to (" << maxX << "," << maxY << ").\n";

        if (i == 0)
        {
            minY = 800;
            maxY = 1600;
        }
        else if (i == 1)
        {
            minX = 800;
            maxX = 1600;
            minY = 0;
            maxY = 800;
        }
        else if (i == 2)
        {
            minX = 800;
            maxX = 1600;
            minY = 800;
            maxY = 1600;
        }
    }

    for (auto &th : workers)
    {
        th.join();
    }

    std::cout << "Saving to image...\n";
    image.save("mandelbrot.ppm");

    std::cout << "Finished.\n";
    return 0;
}

生成的图像:

你可以看到前半部分的一部分被计算了,但大部分都保持黑色,实际上它被第三和第四线程“覆盖”了。

这可能是一个非常明显的错误,但我无法弄清楚。

如果我尝试在没有多线程的情况下执行此操作,例如:

calculateImage(image, width, height, minX, maxX, minY, maxY);

效果很好。

【问题讨论】:

  • 您正在通过引用将看起来像边界的内容传递给子图像(minXmaxX 等),同时您正在更改 main 中的边界。这意味着每个线程将看到相同的边界并渲染到相同的区域。我建议按值传递所有int 参数,除非它们真的需要由所有线程共享。
  • 图像数据也未初始化,因此不是isInside 的值将包含垃圾。

标签: c++ multithreading mandelbrot ppm


【解决方案1】:

您正在将对 minXminYmaxXmaxY 的引用传递给线程,但在启动它们后立即更改它们的值。

所有线程都将引用main 中的相同变量。所有线程都可以观察到更改值。

改为按值传递四个变量。

【讨论】:

  • 是的,这就是答案。刚刚从函数中的 minX、maxX、minY、maxY 和线程部分的 std::ref 部分中删除了与号。它现在完美运行。似乎在线程启动后立即更改值没有提供预期的结果。
  • @P.Protas 我对widthheight 的建议相同,因为您以后可能会遇到同样的问题。如果没有理由按引用传递,则按值传递,尤其是在线程之间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-05
  • 1970-01-01
  • 1970-01-01
  • 2021-05-04
相关资源
最近更新 更多