We have a simple WCF service that is tagged with InstanceContextMode = Single and ConcurrencyMode = Multiple. For methods that return Lists, rather than going out to our database and filling the list when the call is made, we simply return a local copy of the list. The list is maintained by a background thread that goes out to our database every 24 hours and repopulates the list. We handle the concurrency issues by locking on an object in 2 places: inside the method returning the collection, and inside the method filling the collection.
My question is, how can we make this more efficient. Currently, we have a bottleneck in out service method “GetCustomers” that the client will call:
public List<ZGpCustomer> GetCustomers()
{
List<ZGpCustomer> customerListCopy = new List<ZGpCustomer>();
lock (_customerLock)
{
customerListCopy = new List<ZGpCustomer>(_customers);
}
return customerListCopy;
}
Since “_customers” is being filled only every 24 hours, it seems like we should only every need to lock inside the GetCustomers method ONLY when we’re modifying the collection. The way it is currently set up, if 1000 requests come in at the same time, we’re effectively queuing the 1000 requests since only 1 thread will have access to that method at a time. This is rendering the multithreading aspect of the service somewhat useless, no?
Is there a best practice for this sort of pattern? Should I be using a more appropriate data collection to store my objects in? Would a “BlockingCollection” be a more suitable fit?
There most certainly is a more efficient way of doing this. The trick is to keep the master reference to the collection immutable. That way you never have to synchronize access on the reader side. Only the writer side of things needs a lock. The reader side needs nothing which means the readers stay highly concurrent. The only thing you need to do is mark the
_customersreference as volatile.If you are willing to change the signature of
GetCustomersslightly you could exploit the immutability of_customersand return a readonly wrapper.