2

フレームワークでロック メカニズムを実行するクラスを実装する必要があります。いくつかのスレッドがあり、それらには 0、1、2、3 の番号が付けられていResourceHandlerます。指定されたオブジェクトでこれらのスレッドをロックする という静的クラスがあります。要件は、nLock()回の呼び出しが m 回のRelease()呼び出しによって実現される必要があることです。ここで、n = [0..] および m = [0..] です。そのため、1 つのオブジェクトに対して何回ロックを実行したとしても、Release() すべてのロックを解除するには 1 回の呼び出しで十分です。さらに、オブジェクトがロックされていない場合、Release()call は何も実行しません。また、どのオブジェクトがどのスレッドでロックされているかを知る必要があります。

私はこの実装を持っています:

public class ResourceHandler
{
    private readonly Dictionary<int, List<object>> _locks = new Dictionary<int, List<object>>();

    public static ResourceHandler Instance {/* Singleton */}

    public virtual void Lock(int threadNumber, object obj)
    {
        Monitor.Enter(obj);

        if (!_locks.ContainsKey(threadNumber)) {_locks.Add(new List<object>());}
        _locks[threadNumber].Add(obj);
    }

    public virtual void Release(int threadNumber, object obj)
    {
       // Check whether we have threadN in _lock and skip if not
       var count = _locks[threadNumber].Count(x => x == obj);
       _locks[threadNumber].RemoveAll(x => x == obj);

       for (int i=0; i<count; i++)
       {
           Monitor.Exit(obj);
       }
    }

    // .....
 }

実際、ここで私が心配しているのはスレッドセーフです。スレッドセーフかどうかは実際にはわかりません。それを修正するのは本当に苦痛です。タスクを正しく実行していますか?また、これがスレッドセーフであることを確認するにはどうすればよいですか?

4

4 に答える 4

2

メソッドはターゲットsLockをロックしますが、ディクショナリには任意のスレッドがいつでもアクセスできます。( メソッドとメソッドの両方で) ディクショナリにアクセスするためのプライベート ロック オブジェクトを追加することができます。object_locksLockRelease

また、このような ResourceHandler を使用することで、使用されているすべてのオブジェクトを解放するのは残りのコード (消費するスレッド) の責任であることに注意してください (lock ()たとえば、通常のブロックはその問題をカバーしlockます。解放されます)。

ReferenceEqualsの代わりに、オブジェクトがロックされた回数をカウントするときにも使用できます==

于 2012-07-04T09:13:30.887 に答える
1

ConcurrentDictionaryを使用することで、このクラスがスレッドセーフであることを確認できますが、独自のロックメカニズムを開発しようとすることで発生するすべての問題を解決することはできません。

すでに.NetFrameworkの一部であるロックメカニズムがいくつかあります。それらを使用する必要があります。

目的を達成するには、ウェイトハンドルを含め、これらを組み合わせて使用​​する必要があるようです。


編集

もっと注意深く読んだ後、私はあなたが必要かもしれないと思いますEventWaitHandle

于 2012-07-04T09:12:39.210 に答える
1

あなたが持っているものは概念的に危険に見えます。Monitor.Enterこれは、呼び出しがステートメントMonitor.Exitとして機能するため、ブロックにカプセル化することをお勧めします。つまり、それらが順次実行されるようにするためです。前に呼び出すと、例外がスローされます。Locktry/finallyMonitor.ExitMonitor.Enter

これらの問題を回避するには(例外がスローされた場合、特定のスレッドのロックが取得される場合と取得されない場合があります。ロックが取得された場合、ロックが解放されないため、ロックがリークされます。1つを使用することをお勧めします上記の他の回答で提供されているオプションの.ただし、このメカニズムを続行したい場合は、CLR 4.0 で次のオーバーロードがMonitor.Enterメソッドに追加されました

public static void Enter (object, ref bool lockTaken);

lockTakenEnterメソッドが例外をスローし、ロックが取得されなかった場合にのみ false になります。したがって、グローバルを使用して 2 つのメソッドを使用すると、bool lockTaken次のようなものを作成できます (ここでは、単一のロッカーの例を示しList<bool>ます。スレッドに対応する Dictionary が必要です。または、より良いイベント a Tuple)。したがって、あなたの方法でLockは、次のようなものになります

bool lockTaken = false;
Monitor.Enter(locker, ref lockTaken);

他の方法でRelease

if (lockTaken)
    Monitor.Exit(locker);

これが役立つことを願っています。

編集:私はあなたの問題を十分に理解していないと思いますが、私が収集できることから、Concurrent Collectionを使用しています。これらは完全に安全です。チェックアウトしIProducerConsumerCollection<T>ConcurrentBag<T>. これらは、フレームワークによって処理されるすべてのスレッド safter で、必要なことを容易にするはずです (注: スレッド セーフなコレクションは、それが実行するコードがスレッド セーフであることを意味するわけではありません!)。ただし、このようなコレクションを使用すると、ロックを使用するよりもはるかに遅くなる可能性があります。

于 2012-07-04T09:26:23.473 に答える
0

IMOは、安全にするためにアトミックな関数セットを使用する必要があります。

http://msdn.microsoft.com/en-us/library/system.threading.mutex.aspx

ミューテックスが役立つと思います。

于 2012-07-04T09:03:49.190 に答える