I have a workoing multithreaded mandelbrot implementation. It breaks the workload up into between 1 and 64 threads. No mutex's are required. Here is the worker thread...
```
// Worker thread for processing the Mandelbrot algorithm
DWORD WINAPI MandelbrotWorkerThread(LPVOID lpParam)
{
// This is a copy of the structure from the paint procedure.
// The address of this structure is passed with lParam.
typedef struct ThreadProcParameters
{
int StartPixel;
int EndPixel;
int yMaxPixel;
int xMaxPixel;
uint32_t* BitmapData;
double dxMin;
double dxMax;
double dyMin;
double dyMax;
} THREADPROCPARAMETERS, *PTHREADPROCPARAMETERS;
PTHREADPROCPARAMETERS P;
P = (PTHREADPROCPARAMETERS)lpParam;
// Algorithm obtained from https://en.wikipedia.org/wiki/Mandelbrot_set.
double x0, y0, x, y, xtemp;
int iteration;
// Loop for each pixel in the slice.
for (int Pixel = P->StartPixel; Pixel < P->EndPixel; ++Pixel)
{
// Calculate the x and y coordinates of the pixel.
int xPixel = Pixel % P->xMaxPixel;
int yPixel = Pixel / P->xMaxPixel;
// Calculate the real and imaginary coordinates of the point.
x0 = (P->dxMax - P->dxMin) / P->xMaxPixel * xPixel + P->dxMin;
y0 = (P->dyMax - P->dyMin) / P->yMaxPixel * yPixel + P->dyMin;
// Initial values.
x = 0.0;
y = 0.0;
iteration = 0;
// Main Mandelbrot algorithm. Determine the number of iterations
// that it takes each point to escape the distance of 2. The black
// areas of the image represent the points that never escape. This
// algorithm is supposed to be using complex arithmetic, but this
// is a simplified separation of the real and imaginary parts of
// the point's coordinate. This algorithm is described as the
// naive "escape time algorithm" in the WikiPedia article noted.
while (x * x + y * y <= 2.0 * 2.0 && iteration < max_iterations)
{
xtemp = x * x - y * y + x0;
y = 2 * x * y + y0;
x = xtemp;
++iteration;
}
// When we get here, we have a pixel and an iteration count.
// Lookup the color in the spectrum of all colors and set the
// pixel to that color. Note that we are only ever using 1000
// of the 16777215 possible colors. Changing max_iterations uses
// a different pallette, but 1000 seems to be the best choice.
// Note also that this bitmap is shared by all 64 threads, but
// there is no concurrency conflict as each thread is assigned
// a different region of the bitmap. The user has the option of
// using the original RGB or the new and improved Log HSV system.
if (!bUseHSV)
{
// The old RGB system.
P->BitmapData[Pixel] = ReverseRGBBytes
((COLORREF)(-16777215.0 / max_iterations * iteration + 16777215.0));
}
else
{
// The new HSV system.
sRGB rgb;
sHSV hsv;
hsv = mandelbrotHSV(iteration, max_iterations);
rgb = hsv2rgb(hsv);
P->BitmapData[Pixel] =
(((int)(rgb.r * 255))) +
(((int)(rgb.g * 255)) << 8) +
(((int)(rgb.b * 255)) << 16 );
}
}
// End of thread execution. The return value is available
// to the invoking thread, but we don't presently use it.
return 0;
}
```
and here is the thread dispatcher...
```
// Parameters for each thread.
typedef struct ThreadProcParameters
{
int StartPixel;
int EndPixel;
int yMaxPixel;
int xMaxPixel;
uint32_t* BitmapData;
double dxMin;
double dxMax;
double dyMin;
double dyMax;
} THREADPROCPARAMETERS, *PTHREADPROCPARAMETERS;
// Allocate per thread parameter and handle arrays.
PTHREADPROCPARAMETERS* pThreadProcParameters = new PTHREADPROCPARAMETERS[Slices];
HANDLE* phThreadArray = new HANDLE[Slices];
// MaxPixel is the total pixel count among all threads.
int MaxPixel = (rect.bottom - tm.tmHeight) * rect.right;
int StartPixel, EndPixel, Slice;
// Main thread dispatch loop. Walk the start and end pixel indices.
for (StartPixel = 0, EndPixel = PixelStepSize, Slice = 0;
(EndPixel <= MaxPixel) && (Slice < Slices);
StartPixel += PixelStepSize, EndPixel = min(EndPixel + PixelStepSize, MaxPixel), ++Slice)
{
// Allocate the parameter structure for this thread.
pThreadProcParameters[Slice] =
(PTHREADPROCPARAMETERS)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, sizeof(THREADPROCPARAMETERS));
if (pThreadProcParameters[Slice] == NULL) ExitProcess(2);
// Initialize the parameters for this thread.
pThreadProcParameters[Slice]->StartPixel = StartPixel;
pThreadProcParameters[Slice]->EndPixel = EndPixel;
pThreadProcParameters[Slice]->yMaxPixel = rect.bottom - tm.tmHeight; // Leave room for the status bar.
pThreadProcParameters[Slice]->xMaxPixel = rect.right;
pThreadProcParameters[Slice]->BitmapData = BitmapData; // Bitmap is shared among all threads.
pThreadProcParameters[Slice]->dxMin = dxMin;
pThreadProcParameters[Slice]->dxMax = dxMax;
pThreadProcParameters[Slice]->dyMin = dyMin;
pThreadProcParameters[Slice]->dyMax = dyMax;
// Create and launch this thread.
phThreadArray[Slice] = CreateThread
(NULL, 0, MandelbrotWorkerThread, pThreadProcParameters[Slice], 0, NULL);
if (phThreadArray[Slice] == NULL)
{
ErrorHandler((LPTSTR)_T("CreateThread"));
ExitProcess(3);
}
} // End of main thread dispatch loop.
// Wait for all threads to terminate.
WaitForMultipleObjects(Slices, phThreadArray, TRUE, INFINITE);
// Deallocate the thread arrays and structures.
for (Slice = 0; Slice < Slices; ++Slice)
{
CloseHandle(phThreadArray[Slice]);
HeapFree(GetProcessHeap(), 0, pThreadProcParameters[Slice]);
}
delete[] phThreadArray;
delete[] pThreadProcParameters;
// Refresh the image with the bitmap.
SetDIBitsToDevice(hdc, 0, 0, rect.right, rect.bottom - tm.tmHeight,
0, 0, 0, rect.bottom - tm.tmHeight, BitmapData, &dbmi, 0);
```
If you want to see the whole program, perhaps to compile it and see how it does on your machine, please take a look at https://github.com/alexsokolek2/mandelbrot.
I have a multi-threaded implementation of Mandelbrot. Instead of scanning X and Y in nested loops, I scan pixels from 0 to width*height. The Mandelbrot worker thread then calculates the logical and real coordinates of the pixel. Here is a subset of the code...
```
// Worker thread for processing the Mandelbrot algorithm
DWORD WINAPI MandelbrotWorkerThread(LPVOID lpParam)
{
// This is a copy of the structure from the paint procedure.
// The address of this structure is passed with lParam.
typedef struct ThreadProcParameters
{
int StartPixel;
int EndPixel;
int yMaxPixel;
int xMaxPixel;
uint32_t* BitmapData;
double dxMin;
double dxMax;
double dyMin;
double dyMax;
} THREADPROCPARAMETERS, *PTHREADPROCPARAMETERS;
PTHREADPROCPARAMETERS P;
P = (PTHREADPROCPARAMETERS)lpParam;
// Algorithm obtained from https://en.wikipedia.org/wiki/Mandelbrot_set.
double x0, y0, x, y, xtemp;
int iteration;
// Loop for each pixel in the slice.
for (int Pixel = P->StartPixel; Pixel < P->EndPixel; ++Pixel)
{
// Calculate the x and y coordinates of the pixel.
int xPixel = Pixel % P->xMaxPixel;
int yPixel = Pixel / P->xMaxPixel;
// Calculate the real and imaginary coordinates of the point.
x0 = (P->dxMax - P->dxMin) / P->xMaxPixel * xPixel + P->dxMin;
y0 = (P->dyMax - P->dyMin) / P->yMaxPixel * yPixel + P->dyMin;
// Initial values.
x = 0.0;
y = 0.0;
iteration = 0;
// Main Mandelbrot algorithm. Determine the number of iterations
// that it takes each point to escape the distance of 2. The black
// areas of the image represent the points that never escape. This
// algorithm is supposed to be using complex arithmetic, but this
// is a simplified separation of the real and imaginary parts of
// the point's coordinate. This algorithm is described as the
// naive "escape time algorithm" in the WikiPedia article noted.
while (x * x + y * y <= 2.0 * 2.0 && iteration < max_iterations)
{
xtemp = x * x - y * y + x0;
y = 2 * x * y + y0;
x = xtemp;
++iteration;
}
// When we get here, we have a pixel and an iteration count.
// Lookup the color in the spectrum of all colors and set the
// pixel to that color. Note that we are only ever using 1000
// of the 16777215 possible colors. Changing max_iterations uses
// a different pallette, but 1000 seems to be the best choice.
// Note also that this bitmap is shared by all 64 threads, but
// there is no concurrency conflict as each thread is assigned
// a different region of the bitmap. The user has the option of
// using the original RGB or the new and improved Log HSV system.
if (!bUseHSV)
{
// The old RGB system.
P->BitmapData[Pixel] = ReverseRGBBytes
((COLORREF)(-16777215.0 / max_iterations * iteration + 16777215.0));
}
else
{
// The new HSV system.
sRGB rgb;
sHSV hsv;
hsv = mandelbrotHSV(iteration, max_iterations);
rgb = hsv2rgb(hsv);
P->BitmapData[Pixel] =
(((int)(rgb.r * 255))) +
(((int)(rgb.g * 255)) << 8) +
(((int)(rgb.b * 255)) << 16 );
}
}
// End of thread execution. The return value is available
// to the invoking thread, but we don't presently use it.
return 0;
```
And this is the thread dispatcher...
```
// Parameters for each thread.
typedef struct ThreadProcParameters
{
int StartPixel;
int EndPixel;
int yMaxPixel;
int xMaxPixel;
uint32_t* BitmapData;
double dxMin;
double dxMax;
double dyMin;
double dyMax;
} THREADPROCPARAMETERS, *PTHREADPROCPARAMETERS;
// Allocate per thread parameter and handle arrays.
PTHREADPROCPARAMETERS* pThreadProcParameters = new PTHREADPROCPARAMETERS[Slices];
HANDLE* phThreadArray = new HANDLE[Slices];
// MaxPixel is the total pixel count among all threads.
int MaxPixel = (rect.bottom - tm.tmHeight) * rect.right;
int StartPixel, EndPixel, Slice;
// Main thread dispatch loop. Walk the start and end pixel indices.
for (StartPixel = 0, EndPixel = PixelStepSize, Slice = 0;
(EndPixel <= MaxPixel) && (Slice < Slices);
StartPixel += PixelStepSize, EndPixel = min(EndPixel + PixelStepSize, MaxPixel), ++Slice)
{
// Allocate the parameter structure for this thread.
pThreadProcParameters[Slice] =
(PTHREADPROCPARAMETERS)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, sizeof(THREADPROCPARAMETERS));
if (pThreadProcParameters[Slice] == NULL) ExitProcess(2);
// Initialize the parameters for this thread.
pThreadProcParameters[Slice]->StartPixel = StartPixel;
pThreadProcParameters[Slice]->EndPixel = EndPixel;
pThreadProcParameters[Slice]->yMaxPixel = rect.bottom - tm.tmHeight; // Leave room for the status bar.
pThreadProcParameters[Slice]->xMaxPixel = rect.right;
pThreadProcParameters[Slice]->BitmapData = BitmapData; // Bitmap is shared among all threads.
pThreadProcParameters[Slice]->dxMin = dxMin;
pThreadProcParameters[Slice]->dxMax = dxMax;
pThreadProcParameters[Slice]->dyMin = dyMin;
pThreadProcParameters[Slice]->dyMax = dyMax;
// Create and launch this thread.
phThreadArray[Slice] = CreateThread
(NULL, 0, MandelbrotWorkerThread, pThreadProcParameters[Slice], 0, NULL);
if (phThreadArray[Slice] == NULL)
{
ErrorHandler((LPTSTR)_T("CreateThread"));
ExitProcess(3);
}
} // End of main thread dispatch loop.
// Wait for all threads to terminate.
WaitForMultipleObjects(Slices, phThreadArray, TRUE, INFINITE);
```
This can be broken up into as many threads as desired. My program, for instance, allows between 1 and 64 threads. For the full program, see https://github.com/alexsokolek2/mandelbrot. Good luck.