Многопоточный код Мандельброта выводит некорректное изображение - PullRequest
1 голос
/ 24 апреля 2020
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <complex>
#include <fstream>
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::chrono::seconds;
using std::complex;
using std::cout;
using std::endl;
using std::this_thread::sleep_for;
using std::ofstream;
using std::thread;
using std::mutex;
using std::condition_variable;
using std::unique_lock;
mutex mutex1;
mutex mutex2;
std::condition_variable done;

typedef std::chrono::steady_clock the_clock;



const int WIDTH1 = 960;
const int HEIGHT1 = 600;
const int WIDTH2 = 1920;
const int HEIGHT2 = 1200;
int finished_threads = 0;


const int MAX_ITERATIONS = 500;


uint32_t image[HEIGHT2][WIDTH2];



struct ThreadArgs { int id; int delay; };
void myThreadFunc(ThreadArgs args)

{
    for (int i = 0; i < 1; i++) {
        sleep_for(seconds(args.delay));
        cout << args.id;
    }
}
void write_tga(const char *filename)
{
    unique_lock<mutex> lock(mutex2);
    while (finished_threads < 2) {
        done.wait(lock);
    }
    ofstream outfile(filename, ofstream::binary);

    uint8_t header[18] = {
        0, // no image ID
        0, // no colour map
        2, // uncompressed 24-bit image
        0, 0, 0, 0, 0, // empty colour map specification
        0, 0, // X origin
        0, 0, // Y origin
        WIDTH2 & 0xFF, (WIDTH2 >> 8) & 0xFF, // width
        HEIGHT2 & 0xFF, (HEIGHT2 >> 8) & 0xFF, // height
        24, // bits per pixel
        0, // image descriptor
    };
    outfile.write((const char *)header, 18);

    for (int y = 0; y < HEIGHT2; ++y)
    {
        for (int x = 0; x < WIDTH2; ++x)
        {
            uint8_t pixel[3] = {
                image[y][x] & 0xFF, // blue channel
                (image[y][x] >> 8) & 0xFF, // green channel
                (image[y][x] >> 16) & 0xFF, // red channel
            };
            outfile.write((const char *)pixel, 3);
        }
    }

    outfile.close();
    if (!outfile)
    {

        cout << "Error writing to " << filename << endl;
        exit(1);
    }
}


// Render the Mandelbrot set into the image array.
// The parameters specify the region on the complex plane to plot.
void compute_mandelbrot(double left, double right, double top, double bottom)

{
    unique_lock<mutex> lock(mutex2);

    for (int y = 0; y < HEIGHT1; ++y)
    {
        for (int x = 0; x < WIDTH1; ++x)
        {

            complex<double> c(left + (x * (right - left) / WIDTH2),
                top + (y * (bottom - top) / HEIGHT2));

            // Start off z at (0, 0).
            complex<double> z(0.0, 0.0);

            // Iterate z = z^2 + c until z moves more than 2 units
            // away from (0, 0), or we've iterated too many times.
            int iterations = 0;
            mutex1.lock();
            while (abs(z) < 2.0 && iterations < MAX_ITERATIONS)
            {
                z = (z * z) + c;

                ++iterations;
            }
            mutex1.unlock();
            if (iterations == MAX_ITERATIONS)
            {

                image[y][x] = 0x000000; // black
            }
            else
            {

                image[y][x] = 0xFFFFFF; // white
                finished_threads = finished_threads + 1;
                done.notify_all();
            }
        }
    }
}
void compute_mandelbrot2(double left2, double right2, double top2, double bottom2)

{
    unique_lock<mutex> lock(mutex2);
    //map <int, int> val = map<int, int>(0, MAX_ITERATIONS);
    //map <int, int> colourval = map<int, int>(0, MAX_ITERATIONS);
    for (int y = HEIGHT1; y < HEIGHT2; ++y)
    {
        for (int x = HEIGHT1; x < WIDTH2; ++x)
        {

            complex<double> c(left2 + (x * (right2 - left2) / WIDTH2),
                top2 + (y * (bottom2 - top2) / HEIGHT2));

            // Start off z at (0, 0).
            complex<double> z(0.0, 0.0);

            // Iterate z = z^2 + c until z moves more than 2 units

            int iterations = 0;
            mutex1.lock();
            while (abs(z) < 2.0 && iterations < MAX_ITERATIONS)
            {
                z = (z * z) + c;

                ++iterations;
            }
            mutex1.unlock();
            if (iterations == MAX_ITERATIONS)
            {
                // z didn't escape from the circle.
                // This point is in the Mandelbrot set.
                image[y][x] = 0x000000; // black
            }
            else
            {
                // z escaped within less than MAX_ITERATIONS
                // iterations. This point isn't in the set.
                image[y][x] = 0xFFFFFF; // white
                finished_threads = finished_threads + 1;
                done.notify_one();
            }
        }
    }
}


int main(int argc, char *argv[])
{

    std::thread myThread;
    std::thread myThread2;
    std::thread myThread3;

    ThreadArgs args;

    myThread = std::thread(compute_mandelbrot, -2.0, 1.0, 1.125, -1.125);
    myThread3 = std::thread(compute_mandelbrot2, -2.0, 1.0, 1.125, -1.125);
    myThread2 = std::thread(write_tga, "output.tga");

    cout << "Please wait..." << endl;

    // Start timing
    the_clock::time_point start = the_clock::now();



    myThread.join();
    myThread3.join();




    // Stop timing
    the_clock::time_point end = the_clock::now();

    // Compute the difference between the two times in milliseconds
    auto time_taken = duration_cast<milliseconds>(end - start).count();
    cout << "Computing the Mandelbrot set took " << time_taken << " ms." << endl;


    myThread2.join();

    return 0;
}

Выше приведена многопоточная версия кода, и она выводит неверную версию набора, где большая часть черного цвета, но некоторые верны, поэтому я не знаю, в чем проблема: Mandlebrot threadaded но в нерезьбовой версии выводится правильный набор Мандельброта Мандельброт не врезан проблема, скорее всего, связана с тем, как я использовал многопоточность: я просто не знаю, что я сделал неправильно. Любая помощь приветствуется.

1 Ответ

3 голосов
/ 24 апреля 2020

ОК, сначала «простые» вещи! У вас есть 'опечатка' в вашей функции compute_mandelbrot2 в операторе управления для вашего внутреннего (x) цикла; Эта строка:

    for (int x = HEIGHT1; x < WIDTH2; ++x)

должна (конечно) быть:

    for (int x = WIDTH1; x < WIDTH2; ++x) // WIDTH1 not HEIGHT1

Теперь для более «тонких» вещей. Вы пытаетесь разделить вычисление на две половины, разбив и диапазоны 'x' и 'y' на две половины. Это не будет работать, так как для этого потребуется четыре потока, каждый из которых имеет отношение к четверти графика. Чтобы использовать только две половины, диапазоны «y» в двух функциях потока должны охватывать весь график (но диапазоны «x» можно правильно разделить на две части).

Таким образом, ваши внешние ('y') l oop управляющие операторы должны охватывать весь диапазон в обеих функциях потока, и они оба должны быть следующими:

for (int y = 0; y < HEIGHT2; ++y) {
    //...

Я проверил ваш код с вышеупомянутыми тремя изменениями, и он выдает правильный образ Мандельброта. Не стесняйтесь просить дальнейших разъяснений и / или объяснений.

...