惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Jina AI
Jina AI
酷 壳 – CoolShell
酷 壳 – CoolShell
小众软件
小众软件
S
Schneier on Security
人人都是产品经理
人人都是产品经理
博客园_首页
L
LangChain Blog
D
Docker
B
Blog
阮一峰的网络日志
阮一峰的网络日志
D
DataBreaches.Net
C
Check Point Blog
WordPress大学
WordPress大学
博客园 - 聂微东
P
Palo Alto Networks Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Tailwind CSS Blog
腾讯CDC
Cisco Talos Blog
Cisco Talos Blog
A
Arctic Wolf
C
Cybersecurity and Infrastructure Security Agency CISA
Help Net Security
Help Net Security
The Last Watchdog
The Last Watchdog
有赞技术团队
有赞技术团队
美团技术团队
aimingoo的专栏
aimingoo的专栏
博客园 - 叶小钗
爱范儿
爱范儿
S
Security @ Cisco Blogs
MyScale Blog
MyScale Blog
C
Cisco Blogs
P
Proofpoint News Feed
I
Intezer
Last Week in AI
Last Week in AI
The Register - Security
The Register - Security
IT之家
IT之家
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
H
Help Net Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Latest news
Latest news
M
MIT News - Artificial intelligence
N
News | PayPal Newsroom
G
Google Developers Blog
Cloudbric
Cloudbric
T
Troy Hunt's Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Recorded Future
Recorded Future
Hugging Face - Blog
Hugging Face - Blog
AWS News Blog
AWS News Blog

博客园 - 赏梅斋

直接访问K2数据库找未归档正在进行中的流程的SQL语句 SharePoint学习笔记1--Asp.net与SharePoint的Session机制 安装Lync Server前端必备组件Wmf2008R2时失败的问题 域环境安装SQLServer时出现“帐户名与安全标识之间无任何映射”的问题 网络发现不能启用的问题 sharepoint 2010 与sqlserver denali的支持性安装测试 SPES2011开始注册了! K2 Blackpearl 4.5 怎样配置SQLUM SharePoint2010企业开发最佳实践(九)---- 事件接收器的最佳做法 SharePoint2010企业开发最佳实践(八)---- SPWeb 对象 SharePoint2010企业开发最佳实践(七)---- SPSite 对象 SharePoint2010企业开发最佳实践(六)---- 用于确保释放对象的编码技术 SharePoint2010企业开发最佳实践(五)---- 查找错误释放的对象 SharePoint2010企业开发最佳实践(四)---- 关于使用可释放的 SharePoint 对象的介绍 SharePoint2010企业开发最佳实践(二)---- 处理大型文件夹和列表 SharePoint2010企业开发最佳实践(一)----在 SharePoint Server 中编写有效代码 修改MOSS服务器名称 赏梅斋最新推出论坛Microsoft Information Worker 2008年3月19日主题讨论日
SharePoint2010企业开发最佳实践(三)---- 对象缓存技术
赏梅斋 · 2011-02-11 · via 博客园 - 赏梅斋

许多开发人员都使用 Microsoft .NET Framework 缓存对象(例如 System.Web.Caching.Cache)帮助更好地利用内存并提高总体系统性能。但是,许多对象都不是“线程安全的”,缓存这些对象会导致应用程序失败,并导致意外或无关的用户错误。

缓存数据和对象

缓存是提高系统性能的一种很好的方法。但是,您必须根据线程安全性的需要权衡缓存的好处,因为有些 SharePoint 对象不是线程安全的,缓存会导致它们行为异常。

缓存非线程安全的 SharePoint 对象

您可能会尝试通过缓存从查询返回的 SPListItemCollection 对象来提高性能和内存利用率。一般来说,这是一种不错的做法;但是,SPListItemCollection 对象包含嵌入的 SPWeb 对象,后者不是线程安全的,不应该进行缓存。

例如,假定 SPListItemCollection 对象缓存在线程中。当其他线程尝试读取该对象时,应用程序会失败或行为异常,因为嵌入的 SPWeb 对象不是线程安全的。有关 SPWeb 对象和线程安全性的详细信息,请参阅 Microsoft.SharePoint.SPWeb 类。

下面一节中的指导介绍在多线程环境中缓存非线程安全的 SharePoint 对象时如何阻止出现问题。

了解线程同步的潜在缺点

您可能不知道代码在多线程环境中运行(默认情况下,Internet Information Services(即 IIS)是多线程的)或如何管理该环境。下面的示例演示有时用于缓存非线程安全的 Microsoft.SharePoint.SPListItemCollection 对象的代码。

不良的编码实践

缓存多个线程可能读取的对象

public void CacheData()
{
   SPListItemCollection oListItems;

   oListItems = (SPListItemCollection)Cache["ListItemCacheName"];
   if(oListItems == null)
   {
      oListItems = DoQueryToReturnItems();
      Cache.Add("ListItemCacheName", oListItems, ..);
   }
}

上例中的缓存用法在功能上是正确的;不过,因为 ASP.NET 缓存对象是线程安全的,所以它引入了潜在的性能问题。如果上例中的查询需要 10 秒钟才能完成,则这段时间内可能会有许多用户同时尝试访问该页面。在这种情况下,所有用户会运行同一查询,这会尝试更新同一缓存对象。如果该同一查询运行 10 次、50 次或 100 次,并且多个线程尝试同时更新同一对象,尤其在多重处理的超线程计算机上,性能问题将变得尤其严重。

要防止多个查询同时访问相同对象,必须按如下所示更改代码。

应用锁

检查是否为空

private static object _lock =  new object();

public void CacheData()
{
   SPListItemCollection oListItems;

   lock(_lock)
   {
      oListItems = (SPListItemCollection)Cache["ListItemCacheName"];
      if(oListItems == null)
      {
         oListItems = DoQueryToReturnItems();
         Cache.Add("ListItemCacheName", oListItems, ..);
     }
   }
}

可以通过将锁放在 if(oListItems == null) 代码块中来稍微提高性能。执行此操作时,无需挂起所有线程便可检查数据是否已缓存。根据查询返回数据所需的时间,仍可能有多个用户在同时运行查询。如果在多处理器计算机上运行,尤其会存在这种情况。请记住,运行的处理器越多,查询运行的时间越长,将锁放在 if() 代码块中引发问题的可能性就越大。要确保另一个线程没有在当前线程有机会进行处理之前创建 oListItems,可以使用以下模式。

应用锁

重新检查是否为空

private static object _lock =  new object();

public void CacheData()
{
   SPListItemCollection oListItems;
       oListItems = (SPListItemCollection)Cache["ListItemCacheName"];
      if(oListItems == null)
      {
         lock (_lock)
         {
              // Ensure that the data was not loaded by a concurrent thread
              // while waiting for lock.
              oListItems = (SPListItemCollection)Cache[“ListItemCacheName”];
              if (oListItems == null)
              {
                   oListItems = DoQueryToReturnItems();
                   Cache.Add("ListItemCacheName", oListItems, ..);
              }
         }
     }
}

如果缓存已经填充,则上述示例的效果会与初始实现一样好。如果缓存尚未填充,并且系统的负载很轻,则获取锁会导致性能稍微下降。当系统的负载很重时,此方法应该能够显著提高性能,因为查询只执行一次而不是多次,并且与同步开销相比,查询的开销通常更昂贵。

这些示例中的代码会挂起 IIS 中运行的关键部分中的所有其他线程,并阻止其他线程访问缓存对象,直到已完全生成该缓存对象。这解决了线程同步问题;但是,代码仍然不正确,因为它缓存的是非线程安全的对象。

要解决线程安全性问题,可以缓存从 SPListItemCollection 对象创建的 DataTable 对象。您可以如下所示修改前面的示例,以便代码从 DataTable 对象获取数据。

良好的编码实践

缓存 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, ..);
           }
   }
}