我编写了一个C#库,它有一个方法来计算并行的多个文本段落中的单词。文本段落作为字符流给出,每次调用getnextchar()
时都会出现随机延迟。我的库方法必须获取这些字符流的数组并返回组合的字频数。为此,我有一个安全共享的字频数据结构和一个线程来读取每个字符流并更新共享集合。当所有线程都完成后,我将数据结构返回给客户端应用程序。
客户端应用程序需要每10秒计算一次组合字的中间结果。为此,我使用委托每10秒回调一次客户端,结果直到所有工作线程都已完成,之后我将最终结果返回给客户端。
我的问题是,当我使用临时结果回调客户端时,我必须锁定我的共享数据结构并等待客户端应用程序从回调中返回,然后才能解除锁定。在回调执行期间,所有工作线程都被阻塞,等待数据结构上的锁定。这看起来不是一件明智的事情,因为我不认为我应该依赖或信任客户端代码以便及时返回,甚至根本不回复。然而,只有我能想到的不依赖于客户端代码的其他方式是制作我的数据结构的副本或快照,并通过回调将其传递给客户端。这是以内存和计算为代价的,但是一旦复制完成,工作人员就可以继续更新共享集合,并且回调可以做任何想做的事情。
我的问题有两个:
1)两个邪恶中较小的一个,允许错误的客户端回调实现阻止工作者,或定期执行昂贵的操作。
2)有没有办法解决这个问题,而上述任何一个都没有?
答案 0 :(得分:2)
我肯定会说两个邪恶中更大的一个是信任客户。在持有锁的同时回调未知代码是导致死锁和更糟糕情况的一种方法。是的,您将支付一笔费用来拍摄结构的快照并将其返回。但是快照的额外内存超过了在握住锁定时呼叫的风险。
我遇到了类似的情况,到目前为止快照并不是问题。如果是这样,你可以找到几种方法来解决这个问题。包括增加调用客户端的频率,这将减少您在给定时间快照的数据量。
另一种方法是使用不可变的数据结构。当您准备好与客户交谈时,只需中断当前版本并将其交给客户端即可。允许后台主题开始构建新主题。
答案 1 :(得分:0)
我最近在编写库时遇到了这样的问题 - 它不完全相同,但基本问题是相同的 - 您可以信任您使用库的客户端多少。如果你可以依靠客户来做某件事情,那么当你能获得更好的表现时你会怎么做?
我们最终做的是拥有两个代码路径和客户端可以在我们的对象上设置的标志,以指示要采用的路径。从本质上讲,这是一个“你可以信任我”的旗帜。如果未设置(默认值),则库将假定它正在处理它无法信任的客户端,并且永远不会让客户端直接引用内部数据结构 - 它总是得到一个副本。但是,如果客户端设置了该标志,那么他们就可以直接访问真实结构,并且必须遵循我们设定的规则(记录在标志和访问者上)。
然而,在你的情况下(事件),听起来你还有另外一个选择 - 两个不同的事件。 '正常'一个(在将结构提供给客户端之前克隆结构),以及保持锁定并为其提供真实结构的“高级”结构。只要确保它清楚地记录下来,如果你使用高级的,你不能使用该引用,除非在事件处理程序期间,它最好快速,因为你这样做是在阻止处理。