Error message

  • Notice: Trying to get property of non-object in filter_default_format() (line 532 of /home/ntroutman/webapps/nt_drupal/modules/filter/filter.module).
  • Notice: Undefined variable: options in filter_process_format() (line 911 of /home/ntroutman/webapps/nt_drupal/modules/filter/filter.module).

C#: Thread Safty with Monitor.TryEnter() and Thread.Sleep(0)

For my Embedded Systems class I'm working on a project where we are doing some webcam capture and video processing/effects work. To make life simple we are using OpenCV, which rocks. Our language of quote-in-quote choice is C# and to work with OpenCV we are using EmguCV wrapper/binding/what-have-you. So, while working on code that has to capture a video frame, process the frame to do face detection and a video effect, then draw a frame to display to the user I decided to throw everything into its own frame. Face detection is slow, so I didn't want it slowing down the rendering, so into a thread with it. We don't have capture every frame, the most recent one will do if we miss some while processing, into a thread with it. And so on.

Now comes the annoying part. I could have sworn the code was stable, completely stable, but one day I started getting random hangs. The program would hang indifinitely and you had to force-quit to get it to die. Hangs are horrible to debug, why, because they don't crash and give you an error with a stack trace. Hence, debugging is a nightmare. Seeing as it was threaded I figured a deadlock of some sort, great, now I have an idea, on to track it down. Yeah, debugging deadlocks is a nightmare.

Tried putting Console.WriteLine()'s through out, and what do you know?!?!?!?! It didn't crash anymore, left it running for an hour, no hangs. Bizzare, no? So, I commented out the prints to the console and bam! it hung with in a few minutes to a few seconds. Well, thats trully strange. After much hair pulling, which included writing to seperate files from each thread instead of the console, and reworking the code to not use "lock(<obj>) { ...}" blocks but instead:

void run() {
    while (true) {
        if (!Monitor.TryEnter(lockobj)) {
            Thread.Sleep(0); // <--- Critical line: This yields to the other threads
            continue;
        }
        try {
            // do something with that needed locking
        }
        finally {
            Monitor.Exit(lockobj);
        }
            // do something else 
    }
}

Now the critical line in there (marked as such) is "Thread.Sleep(0)", otherwise the whole "TryEnter()" thing is pointless. Now this works because each of my threads can be pretty lax about when they execute and ordering isn't important.

Add new comment