0

I'm improving the performance of a 3d engine i created, introducing LockBits and parallel processing. I have a class for the engine with the following method to update a bitmap with the result:

 public void draw() {

        clearbmp();

        // locking bmp
        BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, W, H), ImageLockMode.ReadWrite, bmp.PixelFormat);
        IntPtr FirstPixel = bitmapData.Scan0;
        int bytes = Math.Abs(bitmapData.Stride) * H;
        bpp = Bitmap.GetPixelFormatSize(bmp.PixelFormat) / 8;
        rgbarray = new byte[bytes];
        System.Runtime.InteropServices.Marshal.Copy(FirstPixel, rgbarray, 0, bytes);

        int count = 0;
        for (int k = 0; k < solidos.Count; k++)
        {
            if (solidos[k] != null && solidos[k].draw)
            {
                // separating faces of the solid into different threads
                Parallel.ForEach(solidos[k].side, f =>
                {
                    // ... do the operations...
                });
            }
        }

        // copy the array to the bitmap and unlock
        System.Runtime.InteropServices.Marshal.Copy(rgbarray, 0, FirstPixel, bytes);
        bmp.UnlockBits(bitmapData);

The code runs as intended to generate a image, but fails when the main program requires it to update several times in a quick succession, the error occurs in bmp.UnlockBits(bitmapData) with the excepion "Generic error in GDI+"

From what i gathered from my research I suppose this happens because the method runs a second time before it finished the first one, thus trying to unlock data that is already unlocked.

If that is correct then how can i abort the running thread when the new one is created? The last call is always the important one

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
  • Interrupting a thread can be done in a wide variety of ways, which depend in part in how you are executing the threads in the first place (something you didn't bother to share). See marked duplicate for a simple example. Consider instead using `Task` and `CancellationTokenSource`, which are more modern mechanisms, and will be compatible with `async`/`await`-style code – Peter Duniho Jan 11 '18 at 00:56
  • I might have been unclear, the only part of my code that uses threading (implicitly) is the `Parallel.ForEach`, and that implies that I don't have a handle for the threads. Maybe if call the method from inside a thread in main code it will make it easier to handle the problem? – Leandro Azevedo Jan 11 '18 at 09:48
  • _"I don't have a handle for the threads"_ -- you don't need a handle or reference or any other identifier for a thread, to cancel the code running in that thread. If you look at the marked duplicate, or any other of the many questions on Stack Overflow involving canceling threads, you'll see that the _right_ way to do it is for the code _running in the thread_ to cooperate by monitoring some status value and voluntarily ceasing activity when that value indicates it should. E.g. a `bool`, which when `true` the code simply exits. – Peter Duniho Jan 11 '18 at 19:18
  • I did it as suggested in the duplicate and it worked, thanks for the help – Leandro Azevedo Jan 12 '18 at 09:17

1 Answers1

0
  1. Before starting a new call, wait for the existing call to complete. You could do that by simply using a lock region. This solves the correctness issue. Maybe it's easier to make each computation run into a Task using Task.Run. The resulting Task object is a handle to that computation and can be used to wait for it to complete.
  2. If you want to speed up finishing an old computation run that is no longer needed, add a cancellation mechanism. Here, you could use a volatile bool cancel = false;. Set it to true to cancel. In your parallel loop (and possibly elsewhere), check that boolean variable periodically and terminate if it is found true. You can also use a CancellationTokenSource.
usr
  • 168,620
  • 35
  • 240
  • 369