我有一个用C#编写的枚举器,它看起来像这样:
try
{
ReadWriteLock.EnterReadLock();
yield return foo;
yield return bar;
yield return bash;
}
finally
{
if (ReadWriteLock.IsReadLockHeld)
ReadWriteLock.ExitReadLock();
}
我认为这可能是一个危险的锁定模式,因为ReadWriteLock只会在枚举完成时释放,否则锁定会挂起并且永远不会释放,我是否正确?如果是这样,那么解决这个问题的最佳方法是什么?
答案 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您打算提出的问题(关于枚举方面),但需要考虑另外两点:
ReadWriteLock.EnterReadLock
引发异常会怎样?ReadWriteLock.ExitReadLock
引发异常会怎样?在#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
如果你的处理花费很多时间(每次迭代),首先填充集合然后处理,而不是收益更合理。