【问题标题】:Does the C# Yield free a lock?C# Yield 是否释放锁?
【发布时间】:2011-06-04 05:45:04
【问题描述】:

我有以下方法:

public static IEnumerable<Dictionary<string, object>> GetRowsIter
   (this SqlCeResultSet resultSet)
{
    // Make sure we don't multi thread the database.
    lock (Database)
    {
        if (resultSet.HasRows)
        {
            resultSet.Read();

            do
            {
                var resultList = new Dictionary<string, object>();
                for (int i = 0; i < resultSet.FieldCount; i++)
                {
                    var value = resultSet.GetValue(i);
                    resultList.Add(resultSet.GetName(i), value == DBNull.Value 
                                                                  ? null : value);
                }
                yield return resultList;
            } while (resultSet.Read());
        }
        yield break;
    }

我刚刚添加了lock(Database) 来尝试摆脱一些并发问题。不过我很好奇,yield return 会释放Database 上的锁定,然后在下一次迭代时重新锁定吗?或者Database 会在整个迭代期间保持锁定状态吗?

【问题讨论】:

  • 参见csharpindepth.com/Articles/Chapter6/…中的“和finally…”
  • 这可能是一个非常糟糕的主意。当您可以在获得锁和释放锁之间运行任意代码时,很难控制锁的顺序。这只是要求死锁。
  • @Eric Lippert - 我对多线程编码不太熟悉。 (尽管我越做越好。)我现在知道 lock 语句的“魔力”实际上只是一个 try finally 块,并且 yield 在它处于屈服状态时将事物保持在范围内。考虑到这一点,将锁移动到仅围绕for 循环。这给了我对我需要的 SQL Server CE 访问的限制,而在“关闭”迭代时间期间不锁定。 (至少我希望如此!)
  • 补充 Eric 的评论:获取锁的代码甚至可能在与(试图)释放它的代码不同的线程中运行。

标签: c# locking yield


【解决方案1】:

锁转换为 try/finally(普通 C#)。

在迭代器块(又名 yield)中,“finally”成为 enumerator 的 IDisposable.Dispose() 实现的一部分。当您使用最后一个数据时,也会在内部调用此代码。

“foreach”自动调用 Dispose(),因此如果您使用“foreach”(或常规 LINQ 等)消费,它解锁。

但是,如果调用者直接使用 GetEnumerator()(非常罕见)并且读取所有数据并且调用 Dispose(),那么锁不会被释放。

我将不得不检查它是否获得了终结者;它可能会被 GC 释放,但我不会赌钱。

【讨论】:

    【解决方案2】:

    不,yield return 不会导致任何锁被释放/解锁。 lock 语句将扩展为 try / finally 块,迭代器不会与迭代器方法中的显式 try / finally 区别对待。

    细节有点复杂,但finally 块何时在迭代器方法中运行的基本规则是

    1. 当迭代器被挂起并调用Dispose 时,挂起点范围内的finally 块将运行
    2. 当迭代器正在运行并且代码将触发finally 时,finally 块运行。
    3. 当迭代器遇到yield break 语句时,finallyyield break 点处的块将运行

    【讨论】:

    • @Marc 给出了更明确的答案,它的处理方式与迭代器方法中定义的任何其他 try / finally 没有区别。
    • @Marc 是的,在阅读您的评论后,我的意图更加明确。
    • 是否会将锁移动到仅括住for 循环“修复”它,以便在每次迭代后锁定和解锁?
    • @Vaccano 会阻止在迭代器挂起时持有锁。如果不更好地理解锁存在的原因,很难/不可能确定这是否适合您的应用程序
    【解决方案3】:

    锁定一直有效,直到您超出 lock() 的范围。屈服并没有做到这一点。

    【讨论】:

      【解决方案4】:

      Database 对象将被锁定,直到迭代完成(或迭代器被释放)。

      这可能会导致锁定时间过长,我建议不要这样做。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多