摘要:
本文将阐述在SharePoint2010里如何进行对象的缓存以及需要注意的事项,同时也会介绍优化代码的一些技术。
缓存是传统.NET开发中一种常用的用来改善性能的开发方式,但是在SharePoint里要尤其注意缓存带来的性能改善和线程不安全之间的平衡,有些SharePoint对象并非线程安全类型,做缓存时会导致预料外的异常。比如在读取列表数据的时候将SPListItemCollection缓存起来是一种常见的思维方式,但是由于SPListItemCollection包含一个非安全线程的SPWeb对象,如果将SPListItemCollection直接缓存会导致程序运行错误或者运行异常。
下面的例子显示了缓存SPListItemCollection是非线程安全的。
public void CacheData() { SPListItemCollection items; items = (SPListItemCollection)Cache["AListItemCache"]; if(items == null) { items = DoQueryToReturnItems(); Cache.Add("AListItemCache", items, ...); } }
上面的代码里由于IIS的工作机理是多线程运行的,如果DoQueryToResturnItems()查询需要10秒钟,在这个时间段里如果有很多用户也去做同样的操作,就会导致同样的查询会被同时运行,并且会去修改同样的对象items,这不仅会导致线程不安全的问题,也会导致性能的问题。为了阻止多线程的同步问题,需要加锁:
private static object _lock = new object(); public void CacheData() { SPListItemCollection items; lock(_lock) { items = (SPListItemCollection)Cache["AListItemCache"]; if(items == null) { items = DoQueryToReturnItems(); Cache.Add("AListItemCache", items, ...); } } }
在上面的代码里,可以将锁的位置调整的if(items == null)之后得到一定程度的性能改善:
private static object _lock = new object(); public void CacheData() { SPListItemCollection items; items = (SPListItemCollection)Cache["AListItemCache"]; if(items == null) { lock(_lock) { items = DoQueryToReturnItems(); Cache.Add("AListItemCache", items, ...); } } }
但是这种做法会带来其他的问题,当DoQueryToReturnItems这个方法执行的时间过长的时候有可能会有多个线程在lock外等着,当第一个线程更新完Cache后,第二个线程又会进来查询数据并更新线程,如果还有第三个线程也会做同样的事情。。。可以做出如下改善:
private static object _lock = new object(); public void CacheData() { SPListItemCollection oListItems; oListItems = (SPListItemCollection)Cache["ListItemCacheName"]; if(oListItems == null) { lock (_lock) { oListItems = (SPListItemCollection)Cache[“ListItemCacheName”]; if (oListItems == null) { oListItems = DoQueryToReturnItems(); Cache.Add("ListItemCacheName", oListItems, ..); } } } }
以上的代码虽然解决了缓存的问题,但是仍然不是值得推荐的做法,因为它缓存的是一个线程不安全的对象SPListItemCollection。为了解决线程安全的问题,可以用DataTable来保存数据:
private static object _lock = new object(); public void CacheData() { DataTable oDataTable; SPListItemCollection oListItems; lock(_lock) { oDataTable = (DataTable)Cache["ListItemCacheName"]; if(oDataTable == null) { oListItems = DoQueryToReturnItems(); oDataTable = oListItems.GetDataTable(); Cache.Add("ListItemCacheName", oDataTable, ..); } } }
代码优化:
一种常见的优化:
SPWeb myWeb = SPContext.Current.Web; myWeb.Lists["Tasks"].Title = "List_Title"; myWeb.Lists["Tasks"].Description = "List_Description"; myWeb.Lists["Tasks"].Update();
以下的改良代码只实例化tasks列表一次并存储在myList变量里面,减少了对数据库的访问,提高了性能:
SPWeb myWeb = SPContext.Current.Web; SPList myList = myWeb.Lists["Tasks"]; myList.Title="List_Title"; myList.Description="List_Description"; myList.Update();