web-dev-qa-db-ja.com

モニターとロック

C#のスレッドセーフのためにMonitorクラスまたはlockキーワードを使用するのが適切な場合

EDIT:これまでの回答から、lockMonitorへの一連の呼び出しの省略形であるようです。クラス。ロックコールの略称は何ですか?またはより明示的に、

class LockVsMonitor
{
    private readonly object LockObject = new object();
    public void DoThreadSafeSomethingWithLock(Action action)
    {
        lock (LockObject)
        {
            action.Invoke();
        }
    }
    public void DoThreadSafeSomethingWithMonitor(Action action)
    {
        // What goes here ?
    }
}

更新

あなたのすべての助けに感謝します:あなたがすべて提供した情報のいくつかのフォローアップとして、私は別の質問を投稿しました。あなたはこの分野に精通しているようですので、リンクを投稿しました: ロックされた例外をロックおよび管理するこのソリューションの何が問題になっていますか?

80
smartcaveman

Eric Lippertは彼のブログでこれについて語っています: ロックと例外は混同しません

同等のコードは、C#4.0と以前のバージョンで異なります。


C#4.0では次のとおりです。

bool lockWasTaken = false;
var temp = obj;
try
{
    Monitor.Enter(temp, ref lockWasTaken);
    { body }
}
finally
{
    if (lockWasTaken) Monitor.Exit(temp);
}

ロックが取得されるときに、Monitor.Enterアトミックにフラグを設定することに依存しています。


それ以前は:

var temp = obj;
Monitor.Enter(temp);
try
{
   body
}
finally
{
    Monitor.Exit(temp);
}

これは、Monitor.Entertryの間に例外がスローされないことに依存しています。デバッグコードでは、コンパイラがNOPを挿入して、スレッド間の中断を可能にしたため、この条件に違反したと思います。

82
CodesInChaos

lockは、Monitor.Enterのショートカットであり、try + finallyおよびMonitor.Exitを使用します。十分な場合はロックステートメントを使用します。TryEnterなどが必要な場合は、Monitorを使用する必要があります。

38

ロック文は次と同等です:

Monitor.Enter(object);
try
{
   // Your code here...
}
finally
{
   Monitor.Exit(object);
}

ただし、MonitorはWait()およびPulse()、これは複雑なマルチスレッドの状況でしばしば有用です。

更新

ただし、C#4では実装方法が異なります。

bool lockWasTaken = false;
var temp = obj;
try 
{
     Monitor.Enter(temp, ref lockWasTaken); 
     //your code
}
finally 
{ 
     if (lockWasTaken) 
             Monitor.Exit(temp); 
} 

コメントと リンクをCodeInChaosに感謝

20
Shekhar_Pro

Monitorはより柔軟です。私にとって、モニターを使用するお気に入りのケースは、あなたの順番を待って、そして単にスキップしたくない場合

//already executing? eff it, lets move on
if(Monitor.TryEnter(_lockObject))
{
    //do stuff;
    Monitor.Exit(_lockObject);
}
8
Alex

他の人が言ったように、lockは「と同等」です

_Monitor.Enter(object);
try
{
   // Your code here...
}
finally
{
   Monitor.Exit(object);
}
_

しかし、好奇心から、lockは最初に渡した参照を保持し、変更してもスローしません。 ロックされたオブジェクトを変更することはお勧めできませんそして、あなたはそれをしたくありません。

しかし、科学にとって、これはうまくいきます:

_var lockObject = "";
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
    tasks.Add(Task.Run(() =>
    {
        Thread.Sleep(250);
        lock (lockObject)
        {
            lockObject += "x";
        }
    }));
Task.WaitAll(tasks.ToArray());
_

...そして、これはしません:

_var lockObject = "";
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
    tasks.Add(Task.Run(() =>
    {
        Thread.Sleep(250);
        Monitor.Enter(lockObject);
        try
        {
            lockObject += "x";
        }
        finally
        {
            Monitor.Exit(lockObject);
        }
    }));
Task.WaitAll(tasks.ToArray());
_

エラー:

タイプ 'System.Threading.SynchronizationLockException'の例外が70783sTUDIES.exeで発生しましたが、ユーザーコードでは処理されませんでした

追加情報:オブジェクトの同期メソッドは、非同期のコードブロックから呼び出されました。

これは、Monitor.Exit(lockObject);lockObjectが不変であるために変更されたstringsに作用するためです。その後、非同期のコードブロックから呼び出します。これは単なる楽しい事実です。

6
André Pena

両方とも同じものです。 lockはc sharpキーワードで、Monitorクラスを使用します。

http://msdn.Microsoft.com/en-us/library/ms173179(v = vs.80).aspx

4
RobertoBr

ロックとモニターの基本的な動作(Enter + Exit)はほぼ同じですが、モニターにはより多くの同期可能性を可能にするオプションがあります。

ロックはショートカットであり、基本的な使用法のオプションです。

より多くの制御が必要な場合は、モニターの方が適しています。高度な使用方法(バリア、セマフォなど)には、Wait、TryEnter、Pulseを使用できます。

3
Borja