2011-01-05 4 views
29

私は、次の方法があります。C#歩留まりはロックを解除しますか?

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

を私は試してみて、いくつかのconcurancyの問題を取り除くためにlock(Database)を追加しました。私は不思議ですが、yield returnのロックをDatabaseに解放し、次の繰り返しのときに再ロックしますか?または、Databaseは反復の全期間ロックされたままですか?

+1

http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspxの「最後に...」を参照してください –

+6

これは潜在的に*非常に悪い考えです*。ロックが取得されてロックが解除されたときに、任意のコードを実行できるときに、ロック順序を制御することは非常に困難です。これはデッドロックを求めているだけです。 –

+0

@Eric Lippert - 私はマルチスレッドのコーディングにあまり慣れていません。ロックステートメントの「魔法」は本当に単なる「try finally」ブロックであり、「yield」はyield状態にある間は範囲内にあるものを保持することが分かりました。これを念頭に置いて、 'for'ループだけを囲むようにロックを移動しました。これは、 "オフ"の繰り返し時間中にロックしないうちに必要なSQL Server CEアクセスに制限を与えます。 (少なくとも私は願っています!) – Vaccano

答えて

19

いいえyield returnは、ロックを解放/ロック解除しません。 lockステートメントはtry/finallyブロックに展開され、イテレーターはイテレーターメソッド内の明示的なtry/finallyとは違ってこれを処理しません。

詳細はもう少し複雑であるが、finallyブロックはiteratorメソッドの内部で実行する場合の基本的なルールは時点でスコープ内finallyブロックと呼ばれ

    反復子が中断され
  1. Disposeありますサスペンドが実行されます
  2. イテレータが実行されていて、コードがfinallyのブロック実行をトリガすると、ブロックが実行されます。
  3. 反復子がyield breakの点における範囲にfinallyブロックはデータベースオブジェクト
+0

@Marcは、iteratorメソッドで定義されている他の 'try/finally'とまったく同じように扱われることを、より明確に答えました。 – JaredPar

+0

あなたは編集しましたか?私はそれが少し前にはっきりしていなかったと確信しています:) –

+0

あなたのコメントを読んだあと、@Marc yeahは私の意図をより明示的にするために編集しました。 – JaredPar

5

を実行するyield break文に遭遇した反復が終了するまでロックされます(またはイテレータが配置されています)。

これは、ロックが過度になる可能性があります。このようにすることをお勧めします。

+2

+1に存在する理由をよく理解していなければ、判断が困難/不可能であり、過度のロック期間に同意します。 –

2

ロックはlock()の範囲外になるまで有効です。収穫はそれをしません。

13

ロック/最後に(通常のC#)イテレータブロックで

(別名収率)をしようとする変換、 "ついに" 列挙子のIDisposable.Dispose()の実装の一部になります。このコードは、最後のデータを消費するときにも内部的に呼び出されます。

"foreach"は自動的にDispose()を呼び出します。したがって、foreach(またはregualar LINQなど)で消費する場合は、になります。

しかし、呼び出し側はGetEnumeratorメソッドを使用している場合()を直接(非常にまれ)とdoesntのは、すべてのデータを読み込み、は()ではないコール処分はその後、ロックが解除されることはありませんありません。

ファイナライザがあるかどうかを確認する必要があります。それはかもしれない GCによって解放されるが、私はそれにお金を賭けることはありません。