我需要在网络服务中使用斯坦福解析器。当 SentenceParser 加载一个大对象时,我将确保它是一个单例,但在这种情况下,它是线程安全的(根据http://nlp.stanford.edu/software/parser-faq.shtml不是)。否则如何有效地完成?一种选择是在使用时锁定对象。
知道斯坦福大学的人是如何为http://nlp.stanford.edu:8080/parser/做这件事的吗?
我需要在网络服务中使用斯坦福解析器。当 SentenceParser 加载一个大对象时,我将确保它是一个单例,但在这种情况下,它是线程安全的(根据http://nlp.stanford.edu/software/parser-faq.shtml不是)。否则如何有效地完成?一种选择是在使用时锁定对象。
知道斯坦福大学的人是如何为http://nlp.stanford.edu:8080/parser/做这件事的吗?
如果争用不是一个因素,那么锁定(同步)将是您提到的一种选择,它可能就足够了。
但是,如果存在争用,我会看到三个通用选项。
(1) 每次都实例化它
每次执行解析时只需将其实例化为局部变量。局部变量非常安全。实例化当然不是免费的,但根据具体情况可能是可以接受的。
(2) 使用线程局部变量
如果实例化成本很高,请考虑使用 threadlocals。每个线程都将保留自己的解析器副本,并且解析器实例将在给定线程上重用。然而,Threadlocals 也不是没有问题。Threadlocals 可能不会被垃圾收集而不被设置为 null 或直到持有线程消失。因此,如果它们太多,则会出现内存问题。第二,谨防重复使用。如果这些解析器是有状态的,您需要确保清理并恢复初始状态,以便后续使用 threadlocal 实例不会受到之前使用的副作用的影响。
(3) 池化
通常不再推荐使用池化,但如果对象大小确实很大,以至于您需要对可以允许的实例数量进行硬性限制,那么使用对象池可能是最佳选择。
我不知道斯坦福的人是如何实现他们的服务的,但我会基于消息框架构建这样的服务,例如http://www.rabbitmq.com/。因此,您的前端服务将接收文档并使用消息队列与执行 NLP 解析的多个工作人员进行通信(存储文档和检索结果)。工作人员 - 在完成处理后 - 将结果存储到前端服务使用的队列中。这种架构将允许您在高负载的情况下动态添加新的工作人员。尤其是 NLP 标记需要一些时间——每个文档需要几秒钟。