5

C++ 中的一个常见模式是创建一个包装锁的类——锁要么在创建对象时隐式获取,要么在之后显式获取。当对象超出范围时,dtor 会自动释放锁。是否可以在 C# 中执行此操作?据我了解,不能保证 C# 中的 dtor 何时会在对象超出范围后运行。

澄清:一般的任何锁,自旋锁,ReaderWriterLock,等等。自己调用 Dispose 违背了模式的目的——一旦我们退出作用域就释放锁——无论我们是否在中间调用 return、抛出异常或诸如此类。此外,据我了解,使用仍然只会为 GC 排队对象,而不是立即销毁它......

4

6 回答 6

12

为了放大蒂莫西的回答,lock 语句确实使用监视器创建了一个作用域锁。本质上,这转化为如下内容:

lock(_lockKey)
{
    // Code under lock
}

// is equivalent to this
Monitor.Enter(_lockKey)
try
{
     // Code under lock
}
finally
{
    Monitor.Exit(_lockKey)
}

在 C# 中,您很少将 dtor 用于这种模式(请参阅 using 语句/IDisposable)。您可能在代码中注意到的一件事是,如果在 Monitor.Enter 和 try 之间发生异步异常,则看起来监视器不会被释放。JIT 实际上做了一个特殊的保证,即如果 Monitor.Enter 紧接在 try 块之前,则异步异常将在 try 块之前不会发生,从而确保释放。

于 2008-11-02T12:23:58.787 回答
5

您的理解using是不正确的,这是一种以确定性方式发生作用域操作的方法(不会发生对 GC 的排队)。

C# 提供了lock提供排他锁的关键字,如果您想拥有不同的类型(例如读/写),则必须使用该using语句。

PS这个线程可能会让你感兴趣。

于 2008-11-02T12:25:19.563 回答
4

确实,您并不确切知道 dtor 何时运行……但是,如果您实现 IDisposable 接口,然后使用“使用”块或自己调用“Dispose()”,您将有一个放置代码的地方。

问题:当您说“锁”时,您的意思是线程锁,以便一次只有一个线程可以使用该对象吗?如:

lock (_myLockKey) { ... }

请澄清。

于 2008-11-02T12:03:37.190 回答
3

为了完整起见,还有另一种方法可以在不使用usingand的情况下实现类似的 RAII 效果IDisposable。在 C#using中通常更清晰(有关更多想法,另请参阅此处),但在其他语言(例如 Java)中,甚至在 C# 中,如果using由于某种原因不合适,了解它是有用的。

这是一个叫做“Execute Around”的成语,想法是你调用一个方法来执行前和后的事情(例如锁定/解锁你的线程,或者设置和提交/关闭你的数据库连接等),然后你传入那个方法一个委托,它将实现您希望在两者之间发生的操作。

例如:


funkyObj.InOut( delegate{ System.Console.WriteLine( "middle bit" ); } );

根据 InOut 方法的作用,输出可能类似于:

第一位
中间位
最后一点

正如我所说,这个答案只是为了完整性,之前的usingwith建议IDisposable以及lock关键字在 99% 的情况下都会更好。

遗憾的是,虽然 .Net 在这方面比许多其他现代 OO 语言(我在看你,Java)走得更远,但它仍然让 RAII 负责处理客户端代码(即使用using),而在 C++ 中,析构函数将始终在作用域的末尾运行。

于 2008-11-02T14:29:16.740 回答
1

为什么你首先想要一个作用域锁?假设您有以下代码:

lock(obj) {
    ... some logic goes here
}

try如果在insert 的地方发生了异常lock,这通常意味着你现在有一个损坏的状态,其他线程将继续以损坏的状态工作。最好让程序挂起以发出有关问题的信号。

另一个问题是try会导致一些性能损失,但如果有的话,这通常是小得多的问题。

Jeffrey Richter 特别建议不要使用lock语句。

于 2019-04-03T11:59:12.670 回答
0

我一直被using开发人员记住要做的事实所困扰 - 充其量你会收到一个警告,大多数人从不费心将其提升为错误。所以,我一直在玩弄这样的想法——它迫使客户至少尝试正确地做事。幸运和不幸的是,它是一个闭包,所以客户端仍然可以保留资源的副本,并在以后尝试再次使用它 - 但这段代码至少试图将客户端推向正确的方向......

public class MyLockedResource : IDisposable
{
    private MyLockedResource()
    {
        Console.WriteLine("initialize");
    }

    public void Dispose()
    {
        Console.WriteLine("dispose");
    }

    public delegate void RAII(MyLockedResource resource);

    static public void Use(RAII raii)
    {
        using (MyLockedResource resource = new MyLockedResource())
        {
            raii(resource);
        }
    }

    public void test()
    {
        Console.WriteLine("test");
    }
}

很好的用法:

MyLockedResource.Use(delegate(MyLockedResource resource)
{
    resource.test();
});

使用不当!(不幸的是,这是无法避免的......)

MyLockedResource res = null;
MyLockedResource.Use(delegate(MyLockedResource resource)
{
    resource.test();
    res = resource;
    res.test();
});
res.test();
于 2008-12-03T13:51:49.327 回答