Unintentional run-in with C# thread concurrency
- by geekrutherford
For the first time today we began conducting load testing on a ASP.NET application already in production. Obviously you would normally want to load test prior to releasing to a production environment, but that isn't the point here.
We ran a test which simulated 5 users hitting the application doing the same actions simultaneously. The first few pages visited seemed fine and then things just hung for a while before the test failed. While the test was running I was viewing the performance counters on the server noting that the CPU was consistently pegged at 100% until the testing tool gave up.
Fortunately the application logs all exceptions including those unhandled to the database (thanks to log4net). I checked the log and low and behold the error was:
System.ArgumentException: An item with the same key has already been added.
(The rest of the stack trace intentionally omitted)
Since the code was running with debug on the line number where the exception occured was also provided. I began inspecting the code and almost immediately it hit me, the section of code responsible for the exception is trying to initialize a static class. My next question was how is this code being hit multiple times when I have a rudimentary check already in place to prevent this kind of thing (i.e. a check on a public variable of the static class before entering the initializing routine). The answer...the check fails because the value is not set before other threads have already made it through.
Not being one who consistently works with threading I wasn't quite sure how to handle this problem. Fortunately a co-worker recalled having to lock a section of code in the past but couldn't recall exactly how. After a quick search on Google the solution is as follows:
Object objLock = new Object();
lock(objLock)
{
//logic requiring lock
}
The lock statement takes an object and tells the .NET runtime that the current thread has exclusive access while the code within brackets is executing. Once the code completes, the lock is released for another thread to utilize.
In my case, I only need to execute the inner code once to initialize my static class. So within the brackets I have a check on a public variable to prevent it from being initialized again.