Posted: 6/4/2011
Hi there.
My question is in relation to using Cache and issues with multiple threads.
My scenario is that I've got a List<Product> collection in the Cache["products"].
When it comes to updating the Cache with the latest ones from the database the scenario is:Thread A sees it's time to update the Product list in Cache, locks onto a static string variable to acquire exclusive access to carry out modification. At present I get a local reference to the cache, such as:-List<Product> cachedproducts = (List<Product>)Cache["products"];//retrieve a List<Product> collection of the latest Product objects from BLL/DAL that need to be updated, or any new ones not in cache//loop through the cachedproducts and remove only the outdated Product objects via cachedproducts.Remove(product) //by reference
//add any new products or replacements for dated/removed ones via cachedproducts.Add(product);
The question is, say Thread B comes along and pulls out the List<Product> from cache, simply to display products (not modify), is there a chance that certain Product objects could be missing from cache in between the time Thread A has removed the outdated ones and just before it has added the replacements? The time in which Thread A would loop through and remove outdated ones from cache, then loop through and add the new ones retrieved from the db to replace them would happen so quickly...but wonder if there could be an issue with that.If so, would be it better to rather, find the index of the outdated Product in the List in cache, and instead of calling cahedproducts.Remove(product) do say cachedproducts[3] = the newupdated replacement Product object, this way you don't have that situation, even for a split second where the old one was removed and the replacement was yet to be added?Thanks for any help/thoughts/recommendations on this!
Posted: 6/5/2011
Hi M B,
I must say it's an interesting scenario...
Well, to guarantee synchronized update/display, I would go with your second option... However, as you say if your Thread A works very fast, you can lock it so that other thread won't be able to modify the data or work with it while its locked. So, when Thread B tries to get and display the data, if it's locked, try again after 1 second, all until it gets and displays it.
This is the first thing which cames in my mind when working with threads and want to achieve synchronization...
I will have to make some proof of concept for some other scenarios...
Hope this helps,Hajan
Posted: 6/6/2011
Dear Expert
i want to know how we use make the our site compitable to every resolotion.
Thanks
@Naval, please open separate forum thread for your question.
Thank you.
Posted: 6/7/2011
Hi Hajan,
Thankyou for your reply!
I carried out a test for this scenario, and indeed the Remove + Add after it can pose a problem:-Test.aspx //This page simply displays all products in cacheList<Product> productsincache = (List<Product>)Cache["products"];foreach (Product cachedproduct in cachedproducts){ Response.Write("Product ID: " + cachedproduct.ProductID + "<br />");} Response.Write("hello world");--------------------------------------------------------------TestRemove.aspx //This page finds product with ID 3 and will remove it, sleep for 8 seconds, then add new one to replace itlock (myglobalstring){ List<Product> cachedproducts = (List<Product>)Cache["products"];
Product updatedproduct = new Product(); updatedproduct.ProductID = 3;
Product chosenproduct = null; foreach (Product cachedproduct in cachedproducts) { if (cachedproduct.ProductID == 3) { chosenproduct = cachedproduct; break; } }
if (chosenproduct != null) { cachedproducts.Remove(chosenproduct); System.Threading.Thread.Sleep(8000); //8 seconds sleep cachedproducts.Add(updatedproduct); }}1. First i visited a page to fill the Cache with list of products2. I opened the first browser to visit Test.aspx - and products were in cache, including Product with ID 33. I opened a second browser to visit TestRemove.aspx - and while it was sleeping for 8 seconds, i kept freshing the first browser, during that time Product with ID 3 was missing4. After 8 seconds of sleep for second browser was over, and refreshed first browser again, Product with ID 3 was replaced with new one.This shows that this is a potential problem - usually given how quickly code executes it shouldn't be, but I like to try to avoid any potential unwanted behaviour.
For me this is the first time using Caching, so I'm new to this, but I'm trying to protect against possible unexpected failures. :)I am wondering, rather than Removing outdated Product objects from cache and replacing with newer one from BLL/DAL, would it be just safer to copy across the updated property values to the one already in cache, hence rather than removing and adding objects, you keep the one in cache, but update it's property values to newer ones...no need to remove. It would do the same job I think, but also avoid that Remove + Add problem above? :)Thankyou once again!
Well, if you have performed the lock correctly, this should not happen. Actually, when you lock, if you try to refresh the Test.aspx page, it will load all until the thread.sleep "awakes" in TestRemove.aspx page. Once the lock block executes completely in TestRemove.aspx, the page will load in Test.aspx (if you have refreshed it or made postback while lock block was running in the other page). That way, you ensure proper synchronization.
If I were you, I would go with this way... otherwise, you can still update values directly by accessing the index, but it may cost you in performance if you do that too often.
If you don't have very large list of Products, you can even update the whole list as it is, wihtout need to loop and/or access index of items / specific items in the list.
Regards,Hajan
Posted: 6/8/2011
Thanks for the reply.
I just wanted to confirm. The step-by-step scenario... Assuming the Cache is first filled with a List of Products, which contains Product's with ID's 1,2,3,4
1. Browser A/Thread A goes to Test.aspx and it shows all products - 1,2,3,42. Browser B/Thread B goes to TestRemove.aspx, enters lock it executes cachedproducts.Remove(productobjectwithid3), then the thread sleeps for 8 seconds, while still inside lock.3. Browser A/Thread A is refreshed several times while Browser B/Thread B is sleeping - only cached products 1,2,4 are shown4. Browser B/Thread B awakes and adds new replacement Product with ID 3 to cache, exists lock5. Browser A/Thread A is refreshed again and shows all products again 1,2,4,3
Just wondering if I understood this correctly? The way I see it, since the code in Test.aspx which displays Products in Cache never enters a lock, it simply just displays the Products that are in Cache at the time. From what I understand, for code that simply displays contents of Cache you don't need a lock, as you are not modifying the Cache or its contents...?
Additionally, even though Thread B enters a lock and sleeps, Thread A is free to continue showing contents of Cache as it doesnt enter a lock.
It's an interesting topic this, just wondering if I am understanding it correctly!
Thanks for all the help!
- Mark
If you perform correct lock in Thread B, Thread a should not be able to refresh the Test.aspx page, but once refreshing, it should load all until the block code inside lock in Thread B finishes with executing.
I'm in a hurry, so you can check the following two links:
http://stackoverflow.com/questions/39112/what-is-the-best-way-to-lock-cache-in-asp-net - I just found this thread now, while searching for a tutorial that combines Caching and locking in ASP.NET ;) - seems nice explanation in the best answer there. You can see, what he/she locks is an object. Read more there...
http://www.codeproject.com/KB/aspnet/6WaysLock.aspx - Nice tutorial about general lock strategies, including two tactics - optimistic and pessimistic. Check it.
I will get back to you tomorrow ;)...
Write me back your thoughts after reading the links I posted above.
Posted: 6/11/2011
Hi Hajan,Thanks so much. The first link you posted I actually already came across in my search and implemented a similar method where once you are inside the lock, you double check again incase a thread had locked in just before the second thread and carried out the update, so the second thread doesn't update all over again! Also, that video was excellent. I've read about the optimistic and pessimistic locking before. That video gave fantastic explanation and demonstration of it, much better than other ones I have come across. Very useful.
Thanks again!
Posted: 6/14/2011
I'm glad I've helped :)
Don't forget to mark the post(s) that helped you. That way you will help others when searching for similar answers ;)!
Posted: 6/19/2011
Hi Hajan.
I've marked some, they were actually all helpful!
Thank you again!