锁定和管理锁定exception的解决方案有什么问题?

我的目标是在我的应用程序中使用线程安全function和exception处理的约定。 我对线程管理/multithreading的概念比较陌生。 我使用的是.NET 3.5

在阅读本文http://blogs.msdn.com/b/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix后,我编写了以下帮助方法来包装所有锁定的操作。 aspx ,它是针对这个问题而链接的, Monitor vs lock 。

我的想法是,如果我在我的应用程序中一致地使用此约定,那么编写线程安全代码并在线程安全代码中处理错误将更容易,而不会破坏状态。

 public static class Locking { private static readonly Dictionary CorruptionStateDictionary = new Dictionary(); private static readonly object CorruptionLock = new object(); public static bool TryLockedAction(object lockObject, Action action, out Exception exception) { if (IsCorrupt(lockObject)) { exception = new LockingException("Cannot execute locked action on a corrupt object."); return false; } exception = null; Monitor.Enter(lockObject); try { action.Invoke(); } catch (Exception ex) { exception = ex; } finally { lock (CorruptionLock) // I don't want to release the lockObject until its corruption-state is updated. // As long as the calling class locks the lockObject via TryLockedAction(), this should work { Monitor.Exit(lockObject); if (exception != null) { if (CorruptionStateDictionary.ContainsKey(lockObject)) { CorruptionStateDictionary[lockObject] = true; } else { CorruptionStateDictionary.Add(lockObject, true); } } } } return exception == null; } public static void Uncorrupt(object corruptLockObject) { if (IsCorrupt(corruptLockObject)) { lock (CorruptionLock) { CorruptionStateDictionary[corruptLockObject] = false; } } else { if(!CorruptionStateDictionary.ContainsKey(corruptLockObject)) { throw new LockingException("Uncorrupt() is not valid on object that have not been corrupted."); } else { // The object has previously been uncorrupted. // My thought is to ignore the call. } } } public static bool IsCorrupt(object lockObject) { lock(CorruptionLock) { return CorruptionStateDictionary.ContainsKey(lockObject) && CorruptionStateDictionary[lockObject]; } } } 

我使用LockingException类来简化调试。

  public class LockingException : Exception { public LockingException(string message) : base(message) { } } 

这是一个示例用法类,用于说明我打算如何使用它。

 public class ExampleUsage { private readonly object ExampleLock = new object(); public void ExecuteLockedMethod() { Exception exception; bool valid = Locking.TryLockedAction(ExampleLock, ExecuteMethod, out exception); if (!valid) { bool revalidated = EnsureValidState(); if (revalidated) { Locking.Uncorrupt(ExampleLock); } } } private void ExecuteMethod() { //does something, maybe throws an exception } public bool EnsureValidState() { // code to make sure the state is valid // if there is an exception returns false, return true; } } 

由于TryLockedAction中的竞争,您的解决方案似乎只增加了复杂性:

if (IsCorrupt(lockObject)) { exception = new LockingException("Cannot execute locked action on a corrupt object."); return false; } exception = null; Monitor.Enter(lockObject);
if (IsCorrupt(lockObject)) { exception = new LockingException("Cannot execute locked action on a corrupt object."); return false; } exception = null; Monitor.Enter(lockObject); 

当我们仍在等待Monitor.Enter时,lockObject可能会“损坏”,因此没有保护。

我不确定你想要实现什么行为,但可能有助于分离锁定和状态管理:

class StateManager { public bool IsCorrupted { get; set; } public void Execute(Action body, Func fixState) { if (this.IsCorrupted) { // use some Exception-derived class here. throw new Exception("Cannot execute action on a corrupted object."); } try { body(); } catch (Exception) { this.IsCorrupted = true; if (fixState()) { this.IsCorrupted = false; } throw; } } } public class ExampleUsage { private readonly object ExampleLock = new object(); private readonly StateManager stateManager = new StateManager(); public void ExecuteLockedMethod() { lock (ExampleLock) { stateManager.Execute(ExecuteMethod, EnsureValidState); } } private void ExecuteMethod() { //does something, maybe throws an exception } public bool EnsureValidState() { // code to make sure the state is valid // if there is an exception returns false, return true; } }
class StateManager { public bool IsCorrupted { get; set; } public void Execute(Action body, Func fixState) { if (this.IsCorrupted) { // use some Exception-derived class here. throw new Exception("Cannot execute action on a corrupted object."); } try { body(); } catch (Exception) { this.IsCorrupted = true; if (fixState()) { this.IsCorrupted = false; } throw; } } } public class ExampleUsage { private readonly object ExampleLock = new object(); private readonly StateManager stateManager = new StateManager(); public void ExecuteLockedMethod() { lock (ExampleLock) { stateManager.Execute(ExecuteMethod, EnsureValidState); } } private void ExecuteMethod() { //does something, maybe throws an exception } public bool EnsureValidState() { // code to make sure the state is valid // if there is an exception returns false, return true; } } 

另外,据我所知,文章的重点是状态管理在并发存在时更难。 但是,它仍然只是与锁定正交的对象状态正确性问题,可能需要使用完全不同的方法来确保正确性。 例如,不是使用锁定的代码区域更改某些复杂状态,而是创建一个新的,如果成功,只需在单个简单的引用分配中切换到新状态:

public class ExampleUsage { private ExampleUsageState state = new ExampleUsageState(); public void ExecuteLockedMethod() { var newState = this.state.ExecuteMethod(); this.state = newState; } } public class ExampleUsageState { public ExampleUsageState ExecuteMethod() { //does something, maybe throws an exception } }
public class ExampleUsage { private ExampleUsageState state = new ExampleUsageState(); public void ExecuteLockedMethod() { var newState = this.state.ExecuteMethod(); this.state = newState; } } public class ExampleUsageState { public ExampleUsageState ExecuteMethod() { //does something, maybe throws an exception } } 

就个人而言,我总是倾向于认为手动锁定足够难以在需要时单独处理每个案例(因此在通用状态管理解决方案中没有太多需要)和低水平的工具来真正谨慎地使用它。

虽然它看起来很可靠,但我有三个顾虑:

1)Invoke()对每个锁定动作的性能成本可能很高。 2)如果动作(方法)需要参数怎么办? 需要一个更复杂的解决方案。 3)CorruptionStateDictionary是否无休止地成长? 我认为uncorrupt()方法应该删除对象而不是将数据设置为false。

  1. 在Try中移动IsCorrupt测试和Monitor.Enter
  2. 将损坏集处理从finally移出并进入Catch块(这应该仅在抛出exception时执行)
  3. 在设置损坏标志之前不要释放主锁(将其保留在最终块中)
  4. 不要将execption限制为调用线程; 通过将bool替换为自定义execption来重新创建它或将其添加到coruption字典中,并使用IsCorrupt Check返回它
  5. 对于Uncorrupt,只需删除该项目
  6. 锁定排序存在一些问题(见下文)

这应该涵盖所有基础

  public static class Locking { private static readonly Dictionary CorruptionStateDictionary = new Dictionary(); private static readonly object CorruptionLock = new object(); public static bool TryLockedAction(object lockObject, Action action, out Exception exception) { var lockTaken = false; exception = null; try { Monitor.Enter(lockObject, ref lockTaken); if (IsCorrupt(lockObject)) { exception = new LockingException("Cannot execute locked action on a corrupt object."); return false; } action.Invoke(); } catch (Exception ex) { var corruptionLockTaken = false; exception = ex; try { Monitor.Enter(CorruptionLock, ref corruptionLockTaken); if (CorruptionStateDictionary.ContainsKey(lockObject)) { CorruptionStateDictionary[lockObject] = ex; } else { CorruptionStateDictionary.Add(lockObject, ex); } } finally { if (corruptionLockTaken) { Monitor.Exit(CorruptionLock); } } } finally { if (lockTaken) { Monitor.Exit(lockObject); } } return exception == null; } public static void Uncorrupt(object corruptLockObject) { var lockTaken = false; try { Monitor.Enter(CorruptionLock, ref lockTaken); if (IsCorrupt(corruptLockObject)) { { CorruptionStateDictionary.Remove(corruptLockObject); } } } finally { if (lockTaken) { Monitor.Exit(CorruptionLock); } } } public static bool IsCorrupt(object lockObject) { Exception ex = null; return IsCorrupt(lockObject, out ex); } public static bool IsCorrupt(object lockObject, out Exception ex) { var lockTaken = false; ex = null; try { Monitor.Enter(CorruptionLock, ref lockTaken); if (CorruptionStateDictionary.ContainsKey(lockObject)) { ex = CorruptionStateDictionary[lockObject]; } return CorruptionStateDictionary.ContainsKey(lockObject); } finally { if (lockTaken) { Monitor.Exit(CorruptionLock); } } } } 

我建议的方法是拥有一个带有“inDangerState”字段的lock-state-manager对象。 需要访问受保护资源的应用程序首先使用lock-manager-object获取锁定; 管理器将代表应用程序获取锁定并检查inDangerState标志。 如果已设置,则管理器将抛出exception并在展开堆栈时释放锁定。 否则,管理器将向应用程序返回一个IDisposable,它将释放Dispose上的锁,但也可以操作危险状态标志。 在将锁定的资源置于错误状态之前,应该在IDisposable上调用一个方法,该方法将设置inDangerState并返回一个令牌,一旦锁定的资源恢复到安全状态,该令牌可用于重新清除它。 如果在重新清除inDangerState标志之前IDisposable是Dispose’d,则资源将“卡在”’危险’状态。

可以将锁定资源恢复到安全状态的exception处理程序应该在返回或传播exception之前使用该标记清除inDangerState标志。 如果exception处理程序无法将锁定的资源恢复到安全状态,则应在设置inDangerState时传播exception。

这种模式似乎比你的建议简单,但似乎要好于假设所有exception都会破坏锁定的资源,或者没有任何exception会破坏。