2013-03-04 74 views
3

我的图像库中有以下代码,它利用处理器列表动态地处理由HttpModule捕获的图像。线程安全无锁

目前只创建每个处理器的一个实例,以便减少内存开销,并且每个处理器都有可写属性,这些属性有助于确定处理每个匹配查询字符串参数的顺序并存储解析值以处理。

正如你所看到的,我目前正在将lock语句中的方法功能包装起来,以防止来自HttpModule的不同线程覆盖处理器属性,尽管我知道这可能是瓶颈。我想知道的是:有没有设计模式或方法可以让我的处理器在没有锁的情况下进行线程安全?

public static ImageFactory AutoProcess(this ImageFactory factory) 
{ 
    if (factory.ShouldProcess) 
    { 
     // TODO: This is going to be a bottleneck for speed. Find a faster way. 
     lock (SyncLock) 
     { 
      // Get a list of all graphics processors that 
      // have parsed and matched the querystring. 
      List<IGraphicsProcessor> list = 
       ImageProcessorConfig.Instance.GraphicsProcessors 
       .Where(x => x.MatchRegexIndex(factory.QueryString) != int.MaxValue) 
       .OrderBy(y => y.SortOrder) 
       .ToList(); 

      // Loop through and process the image. 
      foreach (IGraphicsProcessor graphicsProcessor in list) 
      { 
       factory.Image = graphicsProcessor.ProcessImage(factory); 
      } 
     } 
    } 

    return factory; 

} 
+5

您是否有证据证明这是一个瓶颈?不要浪费宝贵的时间来解决你没有的问题的危险和棘手的解决方案。如果它是一个瓶颈,那么解决方案的第一次尝试应该是*尽可能多地在锁外部进行工作*而不是试图*消除锁*。非常规锁定非常快*;我们正在谈论纳秒。竞争锁定价格昂贵;您可以通过在锁内不做太多工作来消除争用。 – 2013-03-04 23:45:17

+0

说实话,我没有。我会想,虽然它会。我已经尽可能地优化了我的ImageFactory类,以便它不需要围绕“Bitmap”进行任何锁定,并且考虑到我可能有可能处理数千个图像,因此没有尽可能优化该流程似乎是浪费。 我其实并不知道他们那么快。 – 2013-03-04 23:49:47

回答

2

生产者消费者队列可能是你感兴趣的。通常,您的HttpModule会接收事件(生产者)并将它们排队到一个或多个IGraphicsProcessor(消费者)实例。

这是规范的,最简单可行的生产者/消费者队列实现:http://www.albahari.com/threading/part4.aspx#_Wait_Pulse_Producer_Consumer_Queue

如果你是在消除锁定的意图,你应该使用无锁队列实现生产者/消费者队列,如System.Collections.Concurrent.ConcurrentQueue<T>实验性功能。 NET 4.0。

+0

刚才我会看看这个。我实际上已经在另一个标签中打开了第一页。 – 2013-03-04 23:53:32

2

要么因为没有多个处理器而需要保持内存压力(然后您需要至少允许给定的处理器完成当前工作),否则您需要完全并发。

除非有明确的理由,否则我会允许每个图像处理器的多个实例。确保处理器尽早释放它正在处理的数据的引用,以便GC以最佳方式运行。这具有简单的优点,并且具有CPU核心的优势。

如果有明确的理由只允许每个处理器的一个实例,则可以通过避免当前锁定的情况来改进代码,直到处理器完成所有处理器的工作。相反,一旦当前项目完成,您可以有一种机制来请求每个处理器需要处理下一个项目。 @安东尼的建议使用生产者/消费者模式似乎是一个坚实的解决方案。请记住,采用这种方法,您仍然会阻碍每个单独过滤器的吞吐量,并且您可能无法优化所有CPU内核。

+0

仅有一个实例背后的其他主要原因之一是可扩展性。我正在加载并在应用程序启动时创建一个'IGraphicsProcessor'接口的所有实例。这个想法是,图书馆可以通过拖放dll的方式进行扩展。 – 2013-03-04 23:56:50

+0

如果实例化实例的开销很大,则可以使用对象池来管理预初始化对象的N个实例。如果实例化一个实例并不昂贵,那么没有理由预先创建它们。你有具体的测量(或对未来插件的期望)初始化开销会很高吗? – 2013-03-05 00:08:01

+0

嗯,没有。任何未来的实现都应该是轻量级的。然而,目前我必须创建并实例化所有这些查询字符串,每次我调用该方法时,都要使用每个正则表达式来解析查询字符串。 – 2013-03-05 00:24:03