2009-08-25 4 views
6

私はコンパイル時のエラーを取得し、*ロック(<integer var>)は許可されませんが、Monitor.Enter(<integer var>)は許可されます。次のコードについては

「int型」lockステートメント

int i = 0; 
lock(i); 

しかし、このためにエラーなしで必要とされる として参照型ではありません。

int i = 0; 
Monitor.Enter(i); 

私は、合併症が発生したために値のタイプをロックに使用しないでくださいボクシングにe。しかし、それがなぜMonitorで動作するのでしょうか。

答えて

15

理由は、ロックが言語構造であり、コンパイラが式に特別なセマンティクスを課すことを選択した理由です。 Monitor.Enterは単にメソッド呼び出しであり、C#コンパイラは何らかの方法で呼び出しを特殊化しないため、通常のオーバーロードの解決とボクシングを行います。

0

lock()は構文要素であるため、Monitor.Enter()はメソッド呼び出しであるため、コンパイラは自動的にボクシングを実行するので、コンパイラは値の型をチェックしてエラーをスローします。

+0

私はチェックがあるとは思わない。 lockは、tryブロック内のMonito.Enterとfinallyブロック内のMonitor.Exitで拡張されていません。 – Sandbox

+1

いいえ、それはそれに拡張されますが、最初にコンパイラスポットあなたが道に愚かされている場合... – ShuggyCoUk

11

は、intには絶対に使用しないでください。理由は、intがボックス化されているためです。ボックス化された値への参照を格納しないと、一時オブジェクトがロックされるため、例外が発生することなくMonitor.Exitにコールすることはできません。

ロックを行う推奨方法は、private readonly objectを作成してロックすることです。静的メソッドの場合はprivate static objectを使用できます。

3

ちょうど好奇心から離れて、あなたはロックされることを必要とする変数 'i'で何をしていますか?すべてあなたのやっインクリメントか何かがある場合はインタロッククラスを使用する方が効率的な場合があります

Interlocked.Increment(i); // i++ in a thread safe manner 

インタロッククラスは、.NETが提供する最軽量のスレッドの同期ツールであり、簡単な増加のために、デクリメントは、読み込み、または交換、それは最善の選択肢です。

あなたが行動のブロックを同期しようとしている場合は、私は単に同期ルートとして使用できるオブジェクトを作成します。

object syncRoot = new object(); 

// ... 

lock(syncRoot) 
{ 
    // put synced behavior here 
} 
+0

純粋に興味のない...インターロッククラスを認識しています。ありがとう。 – Sandbox

5

コンパイラの仕様はthe behaviour of lock like soを定義します。

ロックステートメントの式のコンパイル時の型は、参照型であることが知られている参照型または>型パラメータ(25.1.1)でなければなりません。 式のコンパイル時の型が値型を示すのはコンパイル時エラーです。

それは、それは、それがモニターので

をコンパイルとしてと等価であるかを定義。Exitは、コンパイラーが自動的にintをボクシングするのを妨げず、そのメリー(そして非常に)間違った方法で行くのを妨げることはありません。

lockは、同じように単純な構文糖ではありません。foreachは単純な構文糖ではありません。得られたIL変換は、ではなく、コードの残りの部分にあたかもそれが書かれているかのように提示されていません()。

foreachの場合、結果変数にはILレベルが存在しないにもかかわらず、反復変数を変更することはできません。ロックでは、コンパイラは既知の値型をコンパイルするのを防ぎます。結果のILはこれを気にしません。
理論的には、それはこの出来事の明確な例を見つけたように、コンパイラは、このの親密な知識(および他の)メソッドで「恵まれた」ことができるが、基本的には常にでこれを発見する不可能です:余談として

コンパイル時(別のメソッド、アセンブリ、またはリフレクションから渡されたオブジェクトを考えてください)、そのようなインスタンスを見つけるのを迷惑にすると、生産性が低下する可能性があります。

コンパイラがAPIの内部構造を知っているほど、将来APIを変更する場合の問題が増えます。

たとえば、intをとり、int値に関連付けられたプロセス全体のmutexでロックされたMonitor.Enter()のオーバーロードを追加することができます。
これはモニターの仕様に準拠しています(恐ろしいかもしれませんが)。しかし、古いコンパイラーにとっては大変な問題を引き起こします。

+0

良い答え。ありがとう。 – Sandbox

関連する問題