【发布时间】:2018-11-08 08:34:00
【问题描述】:
我正在使用线程安全的第三方库从历史数据库中检索数据。
典型场景的操作模式如下:
Library instance;
Result[] Process(string[] itemNames) {
var itemsIds = instance.ReserveItems(itemNames);
Result[] results = instance.ProcessItems(itemIds);
instance.ReleaseItems(itemIds);
return results;
}
Library 是一个实例化成本很高的类,因此这里将其用作单例 (instance),它可以完美地对抗多个线程。
但是,我注意到有时结果被标记为失败(“未找到项目”),当多个线程尝试执行 Process 使用共享一些常见项目的 itemNames 数组时。因为该库的文档非常糟糕,所以这是出乎意料的。
通过集中记录,我推断一个线程可以在另一个线程即将处理它的同时释放一个项目。
在给图书馆的供应商发了几封邮件后,我了解到instance 在线程之间共享一个保留项目列表,并且有必要同步调用...
取消编译库的某些部分证实了这一点:有一个类级别的 m_items 列表被 ReserveItems 和 ReleaseItems 使用。
所以我设想以下浪费:
Result[] Process(string[] itemNames) {
lock(instance) {
var itemsIds = instance.ReserveItems(itemNames);
Result[] results = instance.ProcessItems(itemIds);
instance.ReleaseItems(itemIds);
return results;
}
}
但对我来说似乎有点太暴力了。
由于该库在多线程处理不同项目时可以完美运行,如何执行更细粒度的同步并避免性能损失?
编辑 - 2018-11-09
我注意到库的整个
ProcessItems方法体都包含在一个lock 语句中......因此,任何围绕此进行精细同步的尝试都是徒劳的。我最终也将我的
Process方法主体包含在一个 lock 语句中,性能损失 - 正如预期的那样 - 根本无法察觉。
【问题讨论】:
-
这个大锁(实例)很粗糙。您正在一次处理一组项目。这个批处理比迭代项目并在循环中调用“Reserve(singleitem); ProcessItems(singleitem); ReleaseItems(singleitem)”更快吗?如果这具有相同的性能,那么我将迭代项目并仅锁定项目,例如通过拥有当前处理项目的线程安全列表。如果这听起来不错,如果你愿意,我会用一些代码 sn-ps 发布答案。
-
感谢您的想法,我会尝试一下!
-
您也可以考虑使用ReaderWriterLockSlim。你可以在 ReserveItems 和 ProcessItems 上获得一个读锁,释放它并获得一个写锁来调用 ReleaseItems。
标签: c# multithreading thread-synchronization