我在一个名为Loadeable的类中有一个名为Loadeable的锁定对象:
private readonly object _lock;
lock(_lock){ /*...*/ }
语句仅在类 Loadable 中使用,以防止死锁。(注意:_lock对象的访问器是私有的)
由于我仍然想从外部运行锁定的代码,因此我将以下方法添加到 Loadable 类中:
public void RunLockedProcedure(Action Procedure) {
lock(_lock) {
Procedure();
}
}
Loadable 还包含一个名为 IsLoaded
的属性和一个名为 Load(int)
的方法,但我不显示它们,因为它是很多代码并且并不重要。但是,请务必注意,IsLoaded
和Load(int)
访问线程安全数据。
现在想象 Loadable 的子类称为 Item 和 Creation。
整数_creationId
和以下方法是 Item 的成员:
public Creation LazyGetCreation() {
int creationIdCopy = 0;
//The following lambda provides thread-safety inside this instance.
RunLockedProcedure(() => {
if(!IsLoaded) {
throw new InvalidOperationException("A component cannot be loaded if the instance is not loaded");
} else {
if(_creationId < 1)
throw new InvalidOperationException("The loaded Id of the creation component (" + _creationId + ") is less than 1.");
creationIdCopy = _creationId;
if(Creation == null)
Creation = new Creation();
}
});
//We are not in a thread-safe block and thus Creation might already be null again (I had to use the elvis operator)
Creation?.RunLockedProcedure(() => {
if(!Creation.IsLoaded)
Creation.Load(creationIdCopy); //This is a heavy operation and takes a bit time...
});
}
现在让我解释一下这个问题:
猫王运算符很棒,但对Creation(?).RunLockedProcedure(() => { /*...*/ })
的调用实际上应该在上面的线程安全块内执行(它需要加载 item)。
但是后来我遇到了一个问题,即非常重的Creation.Load(creationIdCopy)
也会在锁定的块中执行,从而锁定该项目很长一段时间。我正在寻找一种安全调用Creation.RunLockedProcedure(/*...*/)
但在非锁定范围内执行() => { /*...*/ }
的方法。(必须锁定到创建实例,而不是项目实例)
也许有一个我还不知道的功能,但找到解决方案会很伤人。
谢谢。
线程安全保证可能应该在Load
本身处理,而不是在外部处理。否则,您在调用负载的每个位置都需要它。这样的东西可能适合你:
private object _lock = new object();
public void Load(int someID)
{
if (IsLoaded) //No-locked check, early exit.
return;
lock (_lock) //We think it's not loaded, lock to prevent issues and double check
if (IsLoaded)
return;
else
IsLoaded = true; //Immediately set IsLoaded = true
try
{
//Do loading stuff
} catch (Exception ex) {
lock(_lock)
IsLoaded = false; //We failed to load, set back to false again
throw;
}
}
然后你这样称呼它:
public Creation LazyGetCreation() {
int creationIdCopy = 0;
//The following lambda provides thread-safety inside this instance.
RunLockedProcedure(() => {
if(!IsLoaded) {
throw new InvalidOperationException("A component cannot be loaded if the instance is not loaded");
} else {
if(_creationId < 1)
throw new InvalidOperationException("The loaded Id of the creation component (" + _creationId + ") is less than 1.");
creationIdCopy = _creationId;
if(Creation == null)
Creation = new Creation();
}
});
Creation.Load(creationIdCopy);
}
如果您不喜欢立即设置 IsLoaded
,则可以引入IsLoading
并在创建对象之前检查两者都为 false,并立即将IsLoading
设置为 true。