December 18, 2008

Basic Concepts: SyncLock, Threading and Race Conditions

I thought I'd continue in the same vein as my previous post and explain another programming paradigm that while relatively simple to use once you understand it, is difficult to explain clearly to beginners and consequently very difficult to grasp - SyncLock and synchronizing access of multiple threads to common system resources such as files, objects and variables

Consider this scenario: a kindergarten teacher whose infants are all clamouring for attention. If the children are all speaking at once, nobody can be heard - the teacher's attention can't be in all places at once. Finally the teacher decides she must establish some ground rules and brings in a wooden spoon from home. Having announced to her class that nobody can have her attention unless they have the spoon, order is restored. If the spoon is on the table the first child to grab the spoon can have her attention and once that child has finished what they're saying, they must replace the spoon on the desk, at which point another child [notice I didn't say "next child", I'll come back to that] can grab the spoon and have their turn.

In programming terms, think of the kindergarten teacher as some resource that multiple threads may need access to, maybe an instance of a class, a file, a variable, whatever - it doesn't matter what the resource is except that we need to know that multiple threads (the infants in the kindergarten class) want access to that resource (the teacher) and must be controlled in some manner.

Enter stage left - the spoon, a.k.a. the SyncLock which serves the same purpose in that none of the threads can access the resource unless they have the spoon! What the SyncLock actually does is to essentially say (in my best southern accent) "I got the spoon and the rest o' y'all are gonna hafta wait 'til I'm dun!".

Now, in the meantime, every other thread also attempts to lock the resource by attempting the grab the spoon. But the spoon's gone, someone else already has it. Until the spoon is returned, they all sit and patiently wait like an Eskimo over a hole in the ice. The original thread puts the spoon back where it got it from and the quickest thread to grab it gets to access the resource and do what they have to do with it, and so the cycle continues.

Coming back to the point I made earlier where I said "another child" rather than "next child": This is a common error that new programmers make when they first start attempting to access objects with multiple threads - you cannot guarantee the order in which the threads are able to apply the SyncLock so you must make sure that your code takes this into account.

So what is a race condition?

Imagine one sheet of paper that each of the children can paint on, but only if the paper hasn't already been painted on by someone else. Each child looks at the piece of paper and says - okay, nobody's painted on it yet so now I can paint and they all do...the last child to finish painting sees what they painted over top of everyone else's picture and all the other children are upset because their painting is ruined. When each child made their evaluation about whether they could paint, nobody had and so they could all paint. In the programming world, this scenario is called a race condition... one way we can solve this is to use the spoon (SyncLock).

Let's alter the scenario slightly, you can only check to see if the paper is painted and paint on it if you have the spoon, otherwise you must get another piece of paper and paint on that.

The first child grabs the spoon. They then evaluate that the paper is empty and determine that they can paint on it and do.

In the meantime, the other children (other threads) attempt to grab the spoon, but can't because the first child has it. They all sit patiently and wait for the spoon [like our Eskimo]...eventually the first child finishes painting and returns the spoon. The quickest child to grab the spoon does and realises the paper is already painted. They grab a new piece of paper and start painting. So the cycle continues. Once all the children have finished painting we have a big stack of paintings and everyone's happy because nobody ruined anyone else's picture.

So this explains the concept of synchronizing resource access using SyncLock to prevent race conditions.

No comments:

Post a Comment