我有以下场景:
我正在尝试锁定一个线程,如果该线程的“自定义”id 与已经进入代码锁定部分的线程匹配,但如果 id 不同则不会。
我创建了一些示例代码来解释我想要的行为
class A
{
private static Dictionary<int, object> _idLocks = new Dictionary<int, object>();
private static readonly object _DictionaryLock = new object();
private int _id;
private void A (int id)
{
_id = id;
}
private object getObject()
{
lock (_DictionaryLock)
{
if (!_idLocks.ContainsKey(_id))
_idLocks.Add(_id, new object());
}
lock (_idLocks[_id])
{
if (TestObject.Exists(_id))
return TestObject(_id);
else
return CreateTestObject(_id);
}
}
}
现在这对我扩展的内容 100% 有效,其中 id 示例 1 不检查其对象是否已创建,而另一个 id 为 1 的线程已经忙于创建该对象。
但是拥有两个锁和一个静态字典似乎根本不是正确的做法,所以我希望有人能向我展示一种改进的方法来阻止线程访问代码,前提是该线程是用相同的方式创建的id 为已经忙于执行锁定部分中的代码的人。
我正在查看 ReaderWriterLockSlim 类,但对我来说使用它并没有真正意义,因为我根本不希望在对象 TestObject(id) 仍在创建时读取它。
我不关心锁定线程访问字典。
我试图不惜一切代价避免的是该线程运行的 _id 不应在 CreateTestObject(_id)
内部使用,而已经有一个忙,因为正在使用该 id 创建和删除文件如果两个线程试图访问相同的文件,这将抛出异常
这可以用普通锁修复,但在这种情况下,我仍然希望 _id 当前未在 CreateTestObject(_id)
方法中运行的线程能够在锁。
这都是因为 CreateTestObject 内部发生的事情需要时间,如果线程正在等待访问它,性能将会受到影响。
最佳答案
看起来您正在使用此代码以线程安全的方式填充字典 - 您可以使用 ConcurrentDictionary 吗?相反?
class A {
private static ConcurrentDictionary<int, object> _dictionary = new ConcurrentDictionary<int, object>();
private int _id;
private object GetObject() {
object output = null;
if(_dictionary.TryGetValue(_id, output)) {
return output;
} else {
return _dictionary.GetOrAdd(_id, CreateTestObject(_id));
}
}
}
编辑:如果你想完全消除调用重复 CreateTestObject
方法的可能性,那么你可以在 _dictionary
中存储一个包装器,它延迟设置 object
class Wrapper {
private volatile object _obj = null;
public object GetObj() {
while(_obj == null) {
// spin, or sleep, or whatever
}
return _obj;
}
public void SetObj(object obj) {
_obj = obj;
}
}
class A {
private static ConcurrentDictionary<int, Wrapper> _dictionary = new ConcurrentDictionary<int, Wrapper>();
private int _id;
private object GetObject() {
Wrapper wrapper = null;
if(_dictionary.TryGetValue(_id, wrapper)) {
return wrapper.GetObj();
} else {
Wrapper newWrapper = new Wrapper();
wrapper = _dictionary.GetOrAdd(_id, newWrapper);
if(wrapper == newWrapper) {
wrapper.SetObj(CreateTestObject(_id));
}
return wrapper.GetObj();
}
}
}
只有一个线程能够将新的 Wrapper
放入 _dictionary
中指定的 _id
- 该线程将初始化其中的对象wrapper == newWrapper
条件。 Wrapper#GetObj
旋转直到设置对象,这可以重写为 block 。
关于c# - 需要改进的线程锁定建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30508609/