This blog post is inspired by my observations from the field, including real customers and real applications.
Everybody knows the importance of using proper logic to synchronize data access across multiple threads, it is a very common question during technical interviews. The following example occurred with a number of customers and I would like to share it:
Key details about the architecture:
-
Web applications are multi-threaded by default. Each request is served in a separate thread or even in multiple threads in case you are using asynchronous methods.
-
When you share any data across the requests/threads it needs to be designed to provide concurrent access.
-
Standard types are very well documented in MSDN whether they are thread-safe or not.
-
Usually there are no issues in the synchronization until you put it under high load with a good variety of concurrent requests coming in.
The sample test I am going to use is a very basic ASP.NET application with WebAPI controller running in Azure. The application stores some internal data in the dictionary collection: System.Collections.Generic.Dictionary<string, string>
.
These could be shared application settings, cached values for most commonly used data or anything else. Data is being accessed for both read and modify (add/remove). When I enable my application for monitoring and ran a heavy stress test I saw huge number of very slow calls, stalled requests and occasional failures.
The failures were due to ThreadAbortException and were caused by the IIS aborting the request due to processing timeout.
The error looks very standard unless you take a look into the performance call graph to understand where the time was spent.
As you see above, the test application requests were stuck in Dictionary.FndEntry method, which isn’t something that you would expect–and no one expects that the application could spend two minutes getting a value from the Dictionary.
The way to fix it is very simple–use collections from System.Collections.Concurrent namespace, particularly ConcurrentDictionary<string, string>
in my example. When I changed the code to leverage ConcurrentDictionary it fixed the performance and we didn’t capture any slow calls of failures due to timeouts.
The problem with using Dictionary in the web applications isn’t new but is still very actual, here is the great MSDN article for more information.