'using' statement vs 'try finally'

I've got a bunch of properties which I am going to use read/write locks on. I can implement them either with a try finally or a using clause.

In the try finally I would acquire the lock before the try , and release in the finally . In the using clause, I would create a class which acquires the lock in its constructor, and releases in its Dispose method.

I'm using read/write locks in a lot of places, so I've been looking for ways that might be more concise than try finally . I'm interested in hearing some ideas on why one way may not be recommended, or why one might be better than another.

Method 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();
        }
    }
}

Method 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();
    }
}

From MSDN, using Statement (C# Reference)

The using statement ensures that Dispose is called even if an exception occurs while you are calling methods on the object. You can achieve the same result by putting the object inside a try block and then calling Dispose in a finally block; in fact, this is how the using statement is translated by the compiler. The code example earlier expands to the following code at compile time (note the extra curly braces to create the limited scope for the object):

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

So basically, it is the same code but with a nice automatic null-checks and an extra scope for your variable . The documentation also states that it "ensures the correct use of IDisposable object" so you might as well gets even better framework support for any obscure cases in the future.

So go with option 2.

Having the variable inside a scope that ends immediately after it's no longer needed is also a plus.


I definitely prefer the second method. It is more concise at the point of usage, and less error prone.

In the first case someone editing the code has to be careful not to insert anything between the Acquire(Read|Write)Lock call and the try.

(Using a read/write lock on individual properties like this is usually overkill though. They are best applied at a much higher level. A simple lock will often suffice here since the possibility of contention is presumably very small given the time the lock is held for, and acquiring a read/write lock is a more expensive operation than a simple lock).


Consider the possibility that both solutions are bad because they mask exceptions .

A try without a catch should obviously be a bad idea; see MSDN for why the using statement is likewise dangerous.

Note also Microsoft now recommends ReaderWriterLockSlim instead of ReaderWriterLock.

Finally, note that the Microsoft examples use two try-catch blocks to avoid these issues, eg

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

A simple, consistent, clean solution is a good goal, but assuming you can't just use lock(this){return mydateetc;} , you might reconsider the approach; with more info I'm sure Stack Overflow can help ;-)

链接地址: http://www.djcxy.com/p/63002.html

上一篇: 内存泄漏时发送电子邮件

下一篇: '使用'语句与'最后尝试'