'使用'语句与'最后尝试'

我有一堆属性,我将使用读/写锁。 我可以通过try finallytry finally子句来实现它们。

try finallytry finally我会在try之前获得locking,并在finally释放。 在using子句中,我将创build一个在其构造函数中获取锁的类,并在其Dispose方法中释放它。

我在很多地方使用读/写锁,所以我一直在寻找比try finally更简洁的方法。 我有兴趣听到一些关于为什么不推荐一种方式或为什么可能比另一种更好的想法。

方法1( try finally ):

 static ReaderWriterLock rwlMyLock_m = new ReaderWriterLock(); private DateTime dtMyDateTime_m public DateTime MyDateTime { get { rwlMyLock_m .AcquireReaderLock(0); try { return dtMyDateTime_m } finally { rwlMyLock_m .ReleaseReaderLock(); } } set { rwlMyLock_m .AcquireWriterLock(0); try { dtMyDateTime_m = value; } finally { rwlMyLock_m .ReleaseWriterLock(); } } } 

方法2:

 static ReaderWriterLock rwlMyLock_m = new ReaderWriterLock(); private DateTime dtMyDateTime_m public DateTime MyDateTime { get { using (new ReadLock(rwlMyLock_m)) { return dtMyDateTime_m; } } set { using (new WriteLock(rwlMyLock_m)) { dtMyDateTime_m = value; } } } public class ReadLock : IDisposable { private ReaderWriterLock rwl; public ReadLock(ReaderWriterLock rwl) { this.rwl = rwl; rwl.AcquireReaderLock(0); } public void Dispose() { rwl.ReleaseReaderLock(); } } public class WriteLock : IDisposable { private ReaderWriterLock rwl; public WriteLock(ReaderWriterLock rwl) { this.rwl = rwl; rwl.AcquireWriterLock(0); } public void Dispose() { rwl.ReleaseWriterLock(); } } 

从MSDN, 使用语句(C#参考)

using语句确保即使在对象上调用方法时发生exception,也会调用Dispose。 你可以通过把对象放在一个try块中,然后在finally块中调用Dispose来获得相同的结果。 实际上,这就是编译器如何翻译using语句。 前面的代码示例在编译时展开为以下代码(请注意额外的大括号来为对象创build有限的作用域):

 { Font font1 = new Font("Arial", 10.0f); try { byte charset = font1.GdiCharSet; } finally { if (font1 != null) ((IDisposable)font1).Dispose(); } } 

所以基本上, 它是相同的代码,但有一个很好的自动空检查和一个额外的范围为您的variables 。 该文档还指出,它“确保正确使用IDisposable对象”,所以你不妨得到更好的框架支持,在未来的任何晦涩的情况下。

所以去选项2。

在不再需要的范围内有一个variables,这个variables也是一个加号。

我绝对喜欢第二种方法。 它在使用方面更简洁,不易出错。

在第一种情况下,编辑代码的人必须小心,不要在Acquire(Read | Write)Lock调用和try之间插入任何东西。

(对这样的单个属性使用读/写锁通常是过度的,它们最好应用在更高的层次上,一个简单的锁通常就足够了,因为考虑到锁持有的时间,争用的可能性很小因为并且获取读/写锁是比简单的锁更昂贵的操作)。

考虑两种解决scheme都是不好的,因为它们掩盖了exception

一个没有catchtry显然是一个坏主意, 请参阅MSDN为什么using语句同样是危险的。

另请注意,Microsoft现在推荐使用ReaderWriterLockSlim而不是ReaderWriterLock。

最后,请注意Microsoft示例使用两个try-catch块来避免这些问题,例如

 try { try { //Reader-writer lock stuff } finally { //Release lock } } catch(Exception ex) { //Do something with exception } 

一个简单的,一致的,干净的解决scheme是一个好的目标,但是假设你不能只使用lock(this){return mydateetc;} ,你可能会重新考虑这个方法。 与更多的信息,我相信堆栈溢出可以帮助;-)

我个人尽可能经常使用C#“using”语句,但是为了避免潜在的问题,我还做了一些具体的事情。 为了显示:

 void doSomething() { using (CustomResource aResource = new CustomResource()) { using (CustomThingy aThingy = new CustomThingy(aResource)) { doSomething(aThingy); } } } void doSomething(CustomThingy theThingy) { try { // play with theThingy, which might result in exceptions } catch (SomeException aException) { // resolve aException somehow } } 

请注意,我将“using”语句分隔成一个方法,并将该对象用于带有“try”/“catch”块的另一个方法。 我可以为相关对象嵌套几个“使用”语句(我有时在我的生产代码中使用三四个深度)。

在这些自定义IDisposable类的Dispose()方法中,我捕获exception(但不是错误)并logging它们(使用Log4net)。 我从来没遇到过任何一个例外都可能影响我的处理的情况。 像往常一样,潜在的错误被允许在调用堆栈上传播,并且通常用适当的消息(错误和堆栈跟踪)来logging终止处理。

如果我以某种方式遇到Dispose()期间可能发生重大exception的情况,我将重新devise这种情况。 坦率地说,我怀疑这是否会发生。

同时,“使用”的范围和清理优势使其成为我最喜欢的C#特性之一。 顺便说一下,我使用Java,C#和Python作为我的主要语言,其他许多人也在这里和那里工作,而“使用”是我最喜欢的语言特性之一,因为它是一种实用的日常工作语言。

我喜欢第三个选项

 private object _myDateTimeLock = new object(); private DateTime _myDateTime; public DateTime MyDateTime{ get{ lock(_myDateTimeLock){return _myDateTime;} } set{ lock(_myDateTimeLock){_myDateTime = value;} } } 

在你的两个选项中,第二个选项是最干净的,更容易理解正在发生的事情。

“一堆属性”和locking属性getter和setter级别看起来是错误的。 你的locking太细致了。 在大多数典型的对象使用情况中,您需要确保您获得一个锁来同时访问多个属性。 你的具体情况可能会有所不同,但我有点怀疑。

无论如何,当你访问对象而不是属性时获取locking会大大减less你必须写入的locking代码的数量。

干说:第二个解决scheme。 第一个解决scheme重复使用锁的逻辑,而第二个解决scheme不。

Try / Catch块通常用于exception处理,而使用块则用于确保对象被丢弃。

对于读/写lockingtry / catch可能是最有用的,但你也可以使用两个,如下所示:

 using (obj) { try { } catch { } } 

这样你就可以隐式地调用你的IDisposable接口,并使exception处理变得简洁。

我认为方法2会更好。

  • 在你的属性中更简单,更易读的代码。
  • 由于locking代码不必多次重写,因此不易出错。

虽然我同意上面的许多意见,包括锁的粒度和可疑的exception处理,但问题是方法之一。 让我给你一个很大的理由,为什么我更喜欢使用try {} finally模型…抽象。

我有一个非常相似的模型,只有一个例外。 我定义了一个基本接口ILock,并在其中提供了一个名为Acquire()的方法。 Acquire()方法返回了IDisposable对象,因此意味着只要我正在处理的对象是ILocktypes,就可以用它来执行locking范围。 为什么这很重要?

我们处理许多不同的locking机制和行为。 你的锁对象可能有一个特定的超时使用。 您的锁实现可能是监视器锁,读卡器锁,写入器锁或自旋锁。 但是,从调用者的angular度来看,所有这一切都是无关紧要的,他们所关心的是locking资源的合同是被尊重的,并且locking是以与其实现一致的方式来执行的。

 interface ILock { IDisposable Acquire(); } class MonitorLock : ILock { IDisposable Acquire() { ... acquire the lock for real ... } } 

我喜欢你的模型,但我会考虑隐藏调用者的locking机制。 FWIW,我测量了使用技术的开销与try-finally,分配一次性对象的开销将有2-3%的性能开销。

我很惊讶没有人build议把匿名函数封装成try-finally。 就像使用using语句实例化和处理类的技术一样,这将locking在一个地方。 我自己比较喜欢这个,因为当我想要释放一个锁时,我宁愿阅读“finally”一词,而不是“Dispose”一词。

 class StackOTest { private delegate DateTime ReadLockMethod(); private delegate void WriteLockMethod(); static ReaderWriterLock rwlMyLock_m = new ReaderWriterLock(); private DateTime dtMyDateTime_m; public DateTime MyDateTime { get { return ReadLockedMethod( rwlMyLock_m, delegate () { return dtMyDateTime_m; } ); } set { WriteLockedMethod( rwlMyLock_m, delegate () { dtMyDateTime_m = value; } ); } } private static DateTime ReadLockedMethod( ReaderWriterLock rwl, ReadLockMethod method ) { rwl.AcquireReaderLock(0); try { return method(); } finally { rwl.ReleaseReaderLock(); } } private static void WriteLockedMethod( ReaderWriterLock rwl, WriteLockMethod method ) { rwl.AcquireWriterLock(0); try { method(); } finally { rwl.ReleaseWriterLock(); } } } 

SoftwareJedi,我没有帐户,所以我不能编辑我的答案。

在任何情况下,以前的版本对于一般用途来说都不是很好,因为读取锁总是需要一个返回值。 这解决了这个问题:

 class StackOTest { static ReaderWriterLock rwlMyLock_m = new ReaderWriterLock(); private DateTime dtMyDateTime_m; public DateTime MyDateTime { get { DateTime retval = default(DateTime); ReadLockedMethod( delegate () { retval = dtMyDateTime_m; } ); return retval; } set { WriteLockedMethod( delegate () { dtMyDateTime_m = value; } ); } } private void ReadLockedMethod(Action method) { rwlMyLock_m.AcquireReaderLock(0); try { method(); } finally { rwlMyLock_m.ReleaseReaderLock(); } } private void WriteLockedMethod(Action method) { rwlMyLock_m.AcquireWriterLock(0); try { method(); } finally { rwlMyLock_m.ReleaseWriterLock(); } } } 

实际上,在你的第一个例子中,为了使解决scheme具有可比性,你也可以在那里实现“IDisposable”,并从“Finally”块中调用“Dispose”,而不是直接解锁。 那么你会是“苹果苹果”的实施(和MSIL) – 两个解决scheme(MSIL将是相同的)。 由于增加了范围,因此框架将确保正确使用“IDisposable”(如果您自己实现“IDisposabe”,则后者不太有利),因此仍然认为使用“使用”是个好主意。

以下为ReaderWriterLockSlim类创build扩展方法,允许您执行以下操作:

 var rwlock = new ReaderWriterLockSlim(); using (var l = rwlock.ReadLock()) { // read data } using (var l = rwlock.WriteLock()) { // write data } 

代码如下:

 static class ReaderWriterLockExtensions() { /// <summary> /// Allows you to enter and exit a read lock with a using statement /// </summary> /// <param name="readerWriterLockSlim">The lock</param> /// <returns>A new object that will ExitReadLock on dispose</returns> public static OnDispose ReadLock(this ReaderWriterLockSlim readerWriterLockSlim) { // Enter the read lock readerWriterLockSlim.EnterReadLock(); // Setup the ExitReadLock to be called at the end of the using block return new OnDispose(() => readerWriterLockSlim.ExitReadLock()); } /// <summary> /// Allows you to enter and exit a write lock with a using statement /// </summary> /// <param name="readerWriterLockSlim">The lock</param> /// <returns>A new object that will ExitWriteLock on dispose</returns> public static OnDispose WriteLock(this ReaderWriterLockSlim rwlock) { // Enter the write lock rwlock.EnterWriteLock(); // Setup the ExitWriteLock to be called at the end of the using block return new OnDispose(() => rwlock.ExitWriteLock()); } } /// <summary> /// Calls the finished action on dispose. For use with a using statement. /// </summary> public class OnDispose : IDisposable { Action _finished; public OnDispose(Action finished) { _finished = finished; } public void Dispose() { _finished(); } } 

我傻。 有一种方法可以使locking方法成为每个实例的一部分,而不是像我以前的文章那样是静态的。 现在我更喜欢这个,因为不需要将`rwlMyLock_m'传递给其他类或方法。

 class StackOTest { private delegate DateTime ReadLockMethod(); private delegate void WriteLockMethod(); static ReaderWriterLock rwlMyLock_m = new ReaderWriterLock(); private DateTime dtMyDateTime_m; public DateTime MyDateTime { get { return ReadLockedMethod( delegate () { return dtMyDateTime_m; } ); } set { WriteLockedMethod( delegate () { dtMyDateTime_m = value; } ); } } private DateTime ReadLockedMethod(ReadLockMethod method) { rwlMyLock_m.AcquireReaderLock(0); try { return method(); } finally { rwlMyLock_m.ReleaseReaderLock(); } } private void WriteLockedMethod(WriteLockMethod method) { rwlMyLock_m.AcquireWriterLock(0); try { method(); } finally { rwlMyLock_m.ReleaseWriterLock(); } } }