I want to make a Mandelbrot set, and make the whole process multithreaded. I am trying to do this with the internal thread library (or how that is exactly called). I can't use openmp for some reason. I think that is because of my laptop (mac m1).
The way I am trying to do this is by making a vector with multiple threads in it, and giving each of them a task to do. The task it has to do is calculate if a pixel is diverging or not. I then want to wait for every thread to finish, and put the pixels into the ppm picture.
This is my code:
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <complex>
#include <fstream>
#include <thread>
using namespace std;
double xmin = -2;
double xmax = 1;
double ymin = -1;
double ymax = 1;
float xDist = xmax - xmin;
float yDist = ymax - ymin;
float scaleFactor = xDist/yDist;
int sizeF = 1000;
int amountOfThreads = 1;
vector< bool > threadsOutcome;
vector<std::thread> threads;
void func(int xj, int yi, int p) {
double x = xmin + xj*xDist/(sizeF*scaleFactor);
double y = ymin + yi*yDist/sizeF;
//cout << x << ", " << y << endl;
int max = 1000;
complex<long double> comp(x,y);
complex<long double> complexNUM(0,0);
for(int i = 0; i < max; i++) {
complexNUM = pow(complexNUM,2) + comp;
if (!std::isfinite(abs(complexNUM))) {
threadsOutcome.at(p) = false;
return;
}
}
threadsOutcome.at(p) = true;
return;
}
int main()
{
for (int i = 0; i < amountOfThreads; i++) {
threadsOutcome.push_back(false);
}
ofstream img;
img.open("picture.ppm");
img << "P3" << endl;
img << sizeF*scaleFactor << " " << sizeF << endl;
img << "255" << endl;
std::complex<long double> c0(1, -1);
vector< complex<long double> > re;
vector< complex<long double> > im;
if (!img.is_open())
{
cout << "Program aborted." << endl;
return -1;
} else {
cout << "Starting!" << endl;
}
for (int i = 0; i < sizeF; i++) {
for (int j = 0; j < sizeF*scaleFactor; j++) {
//cout << i << ", " << j << endl;
//cout << j << endl;
//deciding how many pixels are left until the right border
if (sizeF*scaleFactor - j >= amountOfThreads) {
//doing the multiple
for (int k = 0; k < amountOfThreads; k++) {
thread temp(func, j+k, i, k);
temp.detach();
threads.push_back(temp);
//func(j+k, i, k);
}
for (int k = 0; k < amountOfThreads; k++) {
threads.at(k).join();
}
threads.clear();
//reading that vector baby and putting it into the img
for (int l = 0; l < amountOfThreads; l++) {
//cout << "thest" << endl;
if (threadsOutcome.at(l)) {
int r = 0;
int g = 0;
int b = 0;
img << r << " " << g << " " << b << endl;
//cout << r << " " << g << " " << b << endl;
} else if (!threadsOutcome.at(l)) {
int r = 255;
int g = 255;
int b = 255;
img << r << " " << g << " " << b << endl;
//cout << r << " " << g << " " << b << endl;
}
}
j = j + amountOfThreads - 1;
} else {
//doing the multiple
for (int k = 0; k < sizeF*scaleFactor - j; k++) {
func(j+k, i, k);
}
//reading that vector baby and putting it into the img
for (int l = 0; l < sizeF*scaleFactor - j; l++) {
//cout << "thest" << endl;
if (threadsOutcome.at(l)) {
int r = 0;
int g = 0;
int b = 0;
img << r << " " << g << " " << b << endl;
} else if (!threadsOutcome.at(l)) {
int r = 255;
int g = 255;
int b = 255;
img << r << " " << g << " " << b << endl;
}
}
j = sizeF*scaleFactor - 1;
}
}
double percent = i*100.0/sizeF;
cout << percent << "%" << endl;
}
system("open picture.ppm");
return 0;
}
The issue I have right now is the error from line:
thread temp(func, j+k, i, k);
It says:
no matching constructor for initialization of 'std::thread'
What should I change to fix this?
Threads are not copyable so you need to move the temp
thread into the std::vector<std::thread>
.
That means that
threads.push_back(temp); // tries to copy `temp` into the vector
should be
#include <utility>
// ...
threads.push_back(std::move(temp)); // uses the move constructor instead
Unrelated to your question: When you've fixed the above, the program compiles, but it has undefined behavior and crashes for me. Use AddressSanitizer, valgrind, gdb or some other tool to find out more.
Also unrelated to your question: Your computer has a limited number of hardware threads. Creating more threads than what's supported by hardware can be useful when the threads are mainly idle, waiting for some resource. Your threads don't fall into that category, so don't create more threads than supported by your hardware. It'll only add unnecessary context switching between the threads which will most likely slow things down. See std::thread::hardware_concurrency()