opy of count as do each of the threads created to run method A and B. Also notice that there is no communication between any of the threads via the count variable - it really is a different variable on each thread.
The [ThreadStatic] attribute is the most efficient way of creating thread-local storage.
You can also do it the hard way using named or unnamed data slots but you need to keep in mind that there are less efficient and only slightly more useful. In this case you have to allocate the storage yourself and use special storage and retrieval functions. The details are straightforward and documented under Thread.AllocateDataSlot and associated methods.
Now it’s time to move on to a much bigger and more important topic – exclusion.
In principle exclusion is simple to implement.
All you need is a flag that a thread can test to see if the resource is in use. If it is then the thread should wait, forming a queue with other threads if needs be, until the resource is free as indicated by the flag.
In practice implementing the flag so that nothing goes wrong is difficult in the extreme.
If you simply use a Boolean as a flag, for example, consider what happens when two threads test it at more or less the same time to discover that it is set to false and both proceed to set it to true and use the resource it guards.
Not only do you now have two threads using the resource, when one of them has finished it will set the flag back to false and allow other threads to use the resource.
.NET provides a great many different locking facilities – so many that it’s very confusing. Part of the reason for this excess is that the theory of locking has been developed by many different people who each invented their own favourite way of doing the job.
Let’s start with the simplest and most useful locking mechanism the monitor, invented by Per Brinch Hansen in 1972.
Every object in .NET has a monitor associated with it. A thread can acquire or enter the monitor only if no other thread has already acquired it. If it can’t acquire the monitor it simply waits, in a queue of other threads trying to acquire the same monitor, until the monitor is available.
When the thread that has the monitor is finished it has to explicitly exit or release the monitor. Of course the monitor is implemented in such a way that the sort of problems described with simple locking on a flag cannot happen – acquiring a monitor is an atomic operation – however there are other things that can go wrong as we will see.
The first big problem that confronts any programmer wanting to use a monitor is – which object to use for locking. Remember every object has a monitor and so can provide a unique lock restricting access to a resource.
At this point you need to focus on the fact that in many ways it is the code which accesses the resource which is locked and not the resource.
That is, suppose we have a block of code that manipulates a global variable. We clearly don’t want this code to be active on more than one thread at a time so we acquire the lock at the start of the code and release it at the end of the block.
If there are multiple different blocks of code that access the same resource then each of these blocks have to be written to acquire the lock at the start of the code and release it at the end.
You can now see that whatever object you use to provide the lock it has to be accessible to all of the blocks of code that need to use it. The object that you lock on also has to be a fairly obvious one for the job and it shouldn’t be used, by accident, by another block of code to restrict access to another resource.