这是一种危险的锁定模式吗?

时间:2010-06-02 10:20:53

标签: c# locking yield-return

我有一个用C#编写的枚举器,它看起来像这样:

try
{
    ReadWriteLock.EnterReadLock();
    yield return foo;
    yield return bar;
    yield return bash;
}
finally
{
    if (ReadWriteLock.IsReadLockHeld)
        ReadWriteLock.ExitReadLock();
}

我认为这可能是一个危险的锁定模式,因为ReadWriteLock只会在枚举完成时释放,否则锁定会挂起并且永远不会释放,我是否正确?如果是这样,那么解决这个问题的最佳方法是什么?

3 个答案:

答案 0 :(得分:7)

不,finally块将始终执行,除非有人从计算机中拔出插头(well and a few other exceptions)。

public static IEnumerable<int> GetNumbers() {
    try
    {
        Console.WriteLine("Start");
        yield return 1;
        yield return 2;
        yield return 3;
    }
    finally
    {
        Console.WriteLine("Finish");
    }
}

...

foreach(int i in GetNumbers()) {
    Console.WriteLine(i);
    if(i == 2) break;
}

以上的输出将是

  

开始
1
2
完成

请注意,在C#中,您只需编写yield return,而不只是yield。但我猜这只是一个错字。

答案 1 :(得分:3)

我认为David's answered您打算提出的问题(关于枚举方面),但需要考虑另外两点:

  1. 如果ReadWriteLock.EnterReadLock引发异常会怎样?
  2. 如果ReadWriteLock.ExitReadLock引发异常会怎样?
  3. 在#1中,您将不恰当地致电ReadWriteLock.ExitReadLock。在#2中,您可以隐藏已抛出的现有异常(因为finally子句发生,因为主线处理到达try的末尾,因为异常是抛出;在后一种情况下,你可能不想掩盖异常)。在这种特定情况下,这两件事情可能都不太可能,但是您询问了模式,并且作为一种模式,它存在这些问题。

答案 2 :(得分:2)

最后将以任何方式执行,但锁定可能不安全。比较以下方法:

class Program
{
    static IEnumerable<int> meth1()
    {
        try
        {
            Console.WriteLine("Enter");
            yield return 1;
            yield return 2;
            yield return 3;
        }
        finally
        {
            Console.WriteLine("Exit");
        }
    }

    static IEnumerable<int> meth2()
    {
        try
        {
            Console.WriteLine("Enter");
            return new int[] { 1, 2, 3 };
        }
        finally
        {
            Console.WriteLine("Exit");
        }
    }

    static public void Main()
    {
        foreach (int i in meth1())
        {
            Console.WriteLine("In");
        }
        Console.WriteLine();
        foreach (int i in meth2())
        {
            Console.WriteLine("In");
        }   
    }
}

输出是:

Enter
In
In
In
Exit

Enter
Exit
In
In
In

如果你的处理花费很多时间(每次迭代),首先填充集合然后处理,而不是收益更合理。