"using"语句与"try finally"的区别

90

我有一些属性需要使用读/写锁。我可以使用 try finallyusing 语句来实现。

使用 try finally:在 try 前获取锁,在 finally 中释放锁。

使用 using 语句:创建一个类,构造函数中获取锁,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();
        }
    }
}

方法二:

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

1
就像许多答案中已经说明的那样,方法2非常好,但为了避免每次使用锁时在堆上产生垃圾,您应该将ReadLock和WriteLock更改为结构体。即使using语句使用结构体的IDisposable接口,C#也足够聪明,可以避免装箱! - Michael Gehling
15个回答

0

实际上,在您的第一个示例中,为了使解决方案可比较,您还应该在那里实现IDisposable。然后,您将从finally块调用Dispose()而不是直接释放锁。

然后,您将“苹果对苹果”地实现(和MSIL)-智慧(MSIL对于两个解决方案都相同)。仍然可能使用using是一个好主意,因为添加了作用域,并且因为框架将确保正确使用IDisposable(如果您自己实现IDisposable,则后者的效益较小)。


0
我很惊讶没有人建议将try-finally封装在匿名函数中。就像使用语句实例化和处理类一样的技术,这将锁定保持在一个地方。我更喜欢这种方法,因为当我考虑释放锁时,我宁愿读单词“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();
        }
    }
}

0

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

0

虽然我同意上面的评论,包括锁的粒度和可疑的异常处理,但问题在于方法。让我给你一个使用“using”而不是“try {} finally”模型的主要原因... 抽象。

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

我们处理许多不同的锁定机制和行为。您的锁对象可能具有特定的超时时间。您的锁实现可能是监视器锁、读取器锁、写入器锁或自旋锁。然而,从调用者的角度来看,所有这些都是无关紧要的,他们关心的是遵守锁定资源的契约,并且锁以与其实现一致的方式执行它。

interface ILock {
    IDisposable Acquire();
}

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

我喜欢你的模型,但我会考虑将锁定机制隐藏起来,不让调用者看到。顺便说一下,我已经测量了使用技术与 try-finally 的开销,分配可释放对象的开销将产生2-3%的性能开销。


-1
傻我了。通过将锁定方法作为每个实例的一部分(而不是像我之前的帖子中那样静态),可以使这个过程变得更简单。现在我真的更喜欢这种方式,因为没有必要将“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();
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接