Multithreading and c#

Started by
10 comments, last by mutex 14 years ago
Greetings gamedevers!! I recently stumbled upon a problem and i'm hoping someone has the answer :) I have a small form in c# where upon clicking a button an image is drawn in a graphics object derived by the form itself pixel by pixel (the aim of that is to draw the mandelbrot fractal once complete). Now I wanted to have the pixel generation in a multithreaded environment so I divided the pixel space equally between the number of threads that exist. The problem is that since many threads try to draw something in the Graphics object derived by the form, then it crashes since apparently the draw functions cannot be executed in parallel. I tried to enclose the drawing process in between a mutex lock but it killed the benefits of multithreading. So what I ask is if it is possible to draw in a graphics object concurrently. I could write on an image and then show the image but i'd rather show the whole image as it is drawn rather than produce the output instantly after the end of the computation. Thanks in advance
Advertisement
I'm not too familiar with C# so you'll have to adapt the terminology, but what you will want to do is create N distinct graphics objects, for each thread. Then, when all complete the resulting fragments are recombined by the main thread.
Hmmm I didn't know if it is possible to create more than 1 graphics objects. I'll give it a shot and post the results.
looks like i was able to create multiple graphics objects from one form and they accept multithreading draw commands but the overall execution time remains unchanged whether i have 1 thread or more which leads me to believe that the multiple graphics objects use some sort of critical section and hence serialize the whole process. Any ideas would be welcome again, thanks!
Have all the calculations done in parallel and the rendering done in serial once the threads collapse.

each chunk calculates & stores the results
once a chunk is calculates it moves into the render thread and is rendered.

Basically separate queues for each thread, one of which is the rendering thread.

Remember you will gain no performance benefit if your threads are not on separate CPU cores. They should be scheduled automatically by the OS, I'm just saying this so you can double-check to make sure you haven't created more threads than you have physical cores.

-me
Thanks for the reply firstly :)

Thing is that I have thought about it but i do want to show the image being generated as it is calculated rather than presenting an image at the end of the calculation :)

Also, I am aware that actual speedup will come only by the existence of multiple cores but since i have an i3 i hope i can see some speedup if i manage to get the parallelism right
Quote:Original post by 69mij
Thing is that I have thought about it but i do want to show the image being generated as it is calculated rather than presenting an image at the end of the calculation :)


Yeah you can do that as I presented it:

Take your calculation jobs split them into 2 threads (i3 has 2 cores). Each calculation job does the calculation and saves the results.

Once a calculation job it done, it gets put into the render thread's queue. The render thread is running in a loop that looks something like:
//assumes://   (1) your queue is threadsafe//   (2) pop returns NULL if queue is empty//potentially add another contidion here to make sure you don't spend//too much time.  so maybe: && timeElapsed < maxTimePerLoopwhile ( (Job *j = popFromQueue()) != NULL ){    j->render();}SwapBuffers();


Then you get:

1) parallel computation
2) serial rendering
3) display of computations as they happen

You'll have 2 threads running on one thread: likely render thread + a calculation thread. So it's possible that you may want some dynamic job load balancing between your calculate threads. But it'll probably just work fine without worrying about that to begin with.

-me
I'm not sure about how well GDI+ works with multiple threads, with regards to internal locks. Creating multiple Graphics objects from the same Image/Form sounds like a really bad idea, because ultimately those objects will have to serialize. You might be able to create several small Bitmaps, create a Graphics for each one, and then in each thread draw a portion of the final image to the small Bitmaps. Then in your main thread you could composite the smaller images onto the Form.
Quote:Original post by MJP
I'm not sure about how well GDI+ works with multiple threads, with regards to internal locks. Creating multiple Graphics objects from the same Image/Form sounds like a really bad idea, because ultimately those objects will have to serialize. You might be able to create several small Bitmaps, create a Graphics for each one, and then in each thread draw a portion of the final image to the small Bitmaps. Then in your main thread you could composite the smaller images onto the Form.


MJP is correct that GDI+ is not threadsafe when used in this manner - it provides no functionality for synchronization. The solution outlined above will work to get around this.

What might work better is to create a master Bitmap as a buffer, lock the Bitmap and access the buffer directly across the threads, if all that's being done are set pixel operations.

As far as getting it to display in real-time as its drawn rather than back-buffering... I'd assume you would need direct access to the frame buffer to do this with pixel setting operations. You might want to look into SDL.Net.

edit: kudos to Palidine's elegant solution which I didn't read thoroughly before replying.
First off, don't use GDI+ for this. It should be noted that GDI is, in general, quite slow at drawing things, especially per pixel. Your best bet is to do bulk copies from non-GDI storage to GDI.

If you want to take your scene (say a texture) and divvy it up amongst your threads then assign each thread a work queue with a range determined by the host thread. Queue up all of the ranges, and let the ThreadPool class do the dispatching for you (QueueUserWorkItem).

Then, render to an array of Color's or whatever else you want to use. You can use the same array across all threads safely as long as their rendering regions do not overlap (that is: if thread1 does the range [0 -> 15] and thread2 does [16 - 31] then they can both safely access the same array without fear of writing over each other).

Then, once you've queued up all of your work items you have a couple of choices:
1. Have the main thread sit in a messaging wait state until the rendering completes, then once complete do a bulk copy from the backend array to the frontend bitmap, or
2. As each block completes have the main thread render that block to the appropriate part of the frontend bitmap.

Do not just block on the pool until all work items complete though (it'll make your application non-responsive during rendering...which is bad).

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

This topic is closed to new replies.

Advertisement