【问题标题】:Giving an object an exclusive lock on a member object给对象一个成员对象的排他锁
【发布时间】:2012-07-03 02:08:16
【问题描述】:

我的网络应用程序中的每个请求都可以通过 MVC3 自己的依赖注入机制获得一个数据访问对象实例(类型为 UnitofWork)。到目前为止一切顺利。

我正在创建一个 Idisposable UnitofWorkScope 对象来聚合对该数据访问对象的一些存储调用,然后将它们一起调用。实际上 UnitofWorkScope 只控制 UnitofWork 对象,该对象具有将商店添加到列表并稍后调用它们的功能。我相信 UnitofWorkScope 对象应该具有数据访问对象的独占访问权限。

现在的问题是:我想知道是否有人反对在构造函数中使用 Monitor.Enter() 获得排他锁,然后使用 Monitor.Exit 在 dispose 方法中释放();

我描述了我问这个问题的原因,把水弄得一团糟,但请随意评论我在这里放的任何东西。

public class UnitofWorkScope : IDisposable
{
    public UnitofWorkScope(UnitOfWork UnitofWork)
    {
        if (UnitofWork == null)
        {
            throw new ArgumentException("UnitofWork argument null");
        }  
        this._unitofWork = UnitofWork;
        Monitor.Enter(_unitofWork); // obtaining exclusive access to the DAO of this request
        this._unitofWork.AggregateDbChanges = true; //switched back off in dispose method
    }

    private readonly UnitOfWork _unitofWork;

    bool _disposed;

    public void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            _unitofWork.CallFuncList();
            Monitor.Exit(_unitofWork); //releasing the lock
            _disposed = true;
            GC.SuppressFinalize(this);
        }
    }

    public void Dispose()
    {
        Dispose(true);
    }

    ~UnitofWorkScope()
    {
        if (!_disposed)
        {
            Dispose(false);
        }
    }
}

我们的想法是像这样使用这个 UnitofWorkScope:

UnitofWork _unitofWork = Resolver.GetService<UnitofWork>(); //gets the UnitofWork DAO

using (UnitofWorkScope UnitofWorkScope = new UnitofWorkScope(_unitOfWork))
{
    // do a store

    _unitofWork.Store<SomeClass>(_someInstance);

   // do some more stores 

   try
   {
        UnitofWorkScope.Dispose(true); 
   }
   catch (exception ex)
   {
     //try to undo those stores.
   }
} 

【问题讨论】:

  • 只有当所有其他引用 _unitOfWork 引用的地方在调用方法之前也执行相同的锁定时,这才有效。获取对象实例上的锁不会阻止其他线程使用该实例。所有希望共享实例访问权限的线程都必须就他们将使用什么锁来进行同步达成一致,并且使用哪个对象作为锁并不重要,只要它对所有线程都是相同的即可。
  • "我认为 UnitofWorkScope 对象应该具有数据访问对象的独占访问权限。" - 为什么?你用什么来存储你的数据?我认为这个问题的整个前提是可疑的 - 假设您在整个应用程序中使用相同的 UnitOfWork,这将有效地导致串行运行所有请求,这将减少您应用程序中的任何吞吐量。
  • 这种写法让我很紧张。施工时锁定,处置时释放。此外,您希望 dispose 抛出,这几乎总是一件坏事。如果您的 _unitofWork.CallFuncList() 抛出,您将不会在手动调用 dispose 期间释放您的锁。 using 语句将尝试再次处理并导致相同的异常。您的终结器将再次尝试处理并导致另一个异常。
  • Martin,为每个请求创建了一个新的数据访问对象。
  • 如果您为每个请求创建一个新对象,则无需锁定任何内容,除非您正在执行一些多线程处理,即使那样我认为还有更好的选择。每个请求基本上都会在自己的线程上处理,所以你不需要保护多线程访问

标签: c# concurrency dao


【解决方案1】:

是的,这对于实现锁来说并不是一个糟糕的模式。但是:我建议使用稍微不同的 Dispose 版本,以保证即使 _unitofWork.CallFuncList() 抛出异常(您依赖它来检测是否需要执行某种回滚)也会释放锁。

private void Dispose(bool disposing) 
{ 
    if (!_disposed) 
    { 
        try
        {
            _disposed = true; 
            _unitofWork.CallFuncList(); 
        }
        finally
        {
            Monitor.Exit(_unitofWork); //releasing the lock 
            GC.SuppressFinalize(this); 
        }
    } 
} 

但是,您可能希望将“提交”从锁定“释放”逻辑中分离出来,这样您就不必显式调用 Dispose(),而 using 语句会自动为您完成。

public void Commit()
{
    _unitofWork.CallFuncList(); 
}

private void Dispose(bool disposing) 
{ 
    if (!_disposed) 
    { 
        try
        {
            _disposed = true; 
        }
        finally
        {
            Monitor.Exit(_unitofWork); //releasing the lock 
            GC.SuppressFinalize(this); 
        }
    } 
}

然后你可以像这样使用它:

using (var unitofWorkScope = new UnitofWorkScope(_unitOfWork))     
{     
    // do a store     

    _unitofWork.Store<SomeClass>(_someInstance);     

   // do some more stores      

   try     
   {     
        unitofWorkScope.Commit();
   }     
   catch (exception ex)     
   {     
     //try to undo those stores.     
   }     
}  // unitofWorkScope.Dispose() automatically called here

【讨论】:

    猜你喜欢
    • 2011-10-14
    • 2014-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-17
    • 1970-01-01
    相关资源
    最近更新 更多