2016-11-20 8 views
-1

以下のコードでは、KernalRecursiveAutoResetEventという同期制御に使用されるクラスがあります。これには、ロックのためのAutoResetEvent WaitOne()と解放のためのSet()を呼び出すLock and Leaveメソッドがあります。ループをセットアップして、共有リソースを扱うメソッドを呼び出すことができます。メソッドIncrementでは、intを共有リストに追加します。 LeaveはLockよりも速く呼び出されているため、競争相手はfalingです。実行を制御するより良い方法はありますか?現在のスレッドが所有スレッドと等しいかどうかをチェックする条件は、所有スレッドがLock()メソッドで設定される直前に実行されるため、InvalidOperation例外がスローされます。再帰的自動修復での競合状態の解決

ヒント?

 class KernalRecursiveAutoResetEvent : IDisposable 
      { 
       private AutoResetEvent m_lock = new AutoResetEvent(false); 
       private int m_owningThreadId = 0; 
       private int m_recusionCount = 0; 

       public void Lock() 
       { 
        int currentThreadId = Thread.CurrentThread.ManagedThreadId; 

        if (m_owningThreadId == currentThreadId) 
        { 
         m_recusionCount++; 
         return; 
        } 

        m_lock.WaitOne(); 

        m_owningThreadId = currentThreadId; 
        m_recusionCount = 1; 
       } 

       public void Leave() 
       { 
        if (m_owningThreadId != Thread.CurrentThread.ManagedThreadId) 
         throw new InvalidOperationException(); 

        if (--m_recusionCount == 0) 
        { 
         m_owningThreadId = 0; 
         m_lock.Set(); 
        } 
       } 

       public void Dispose() 
       { 
        m_lock.Close(); 
       } 
      } 

    using (var rare = new KernalRecursiveAutoResetEvent()) 
       { 
        for (int i = 0; i < iterations; i++) 
        { 
         var t = new Thread(a => Increment(ref i, rare)); 
         t.Start(); 
         rare.Lock(); 
        } 
       } 

private static void Increment(ref int i, object _lock) 
     { 
      Increment(ref i); 

      if (_lock is KernalRecursiveAutoResetEvent) 
      { 
       var m_lock = _lock as KernalRecursiveAutoResetEvent; 
       m_lock.Leave(); 
      } 
      else if (_lock is KernalModeMutexSimpleWaitLock) 
      { 
       var m_lock = _lock as KernalModeMutexSimpleWaitLock; 
       m_lock.Leave(); 
      } 
      else if (_lock is KernalModeSemaphoreSimpleWaitLock) 
      { 
       var m_lock = _lock as KernalModeSemaphoreSimpleWaitLock; 
       m_lock.Leave(); 
      } 
      else if (_lock is KernalModeSimpleWaitLock) 
      { 
       var m_lock = _lock as KernalModeSimpleWaitLock; 
       m_lock.Leave(); 
      } 
     } 

private static void Increment(ref int i) 
     { 
      i++; 
     } 
+0

"LeaveはLockより速く呼び出されています" - なぜLeaveを呼び出すコードを投稿しないのですか?同期プリミティブを従来のもの(つまり、説明されている正確な機能を持つ 'Monitor')で置き換えることもできますが、コードが' Enter'の前に 'Exit'を呼び出すと問題は残るでしょう。 –

+0

leaveを呼び出すコードは単純ですが、Iをインクリメントし、rare.Leave()を呼び出します(AutoResetEvent.Set()を呼び出します) 問題はMonitor.EnterまたはExit/TryExitを理解していません。それを制御する.FCL静的なMonitor.EnterとLeave/TryLeaveなどのいくつかの根本的な問題があります。これは、ロックしていない理由、競合状態の制御に失敗したためロックしていない理由を理解することではありません。どのようにLeaveが呼び出される前にLock –

答えて

1

これは不必要に複雑です。新しいintをリストに追加するコードのまわりでlock()を使用するだけです。もちろん、ロックしているオブジェクトは、すべてのスレッド間で共通でなければなりません。

例:Incrementインサイド

var lockObj = new object(); 

for (int i = 0; i < iterations; i++) 
{ 
    // what do you mean `red i`, by the way? It's a value type. 
    var t = new Thread(a => Increment(ref i, lockObj)); 
    t.Start(); 
} 

:もちろん

lock (lockObj) 
{ 
    someIntList.Add(i); // or whatever. Only one thread at a time can do this! 
} 

、あなたのlockObjも静的である可能性があります。

また、新しいスレッドを開始しないでください。代わりにTaskを使用してください。

+0

私はMonitor.Enter(ロック)を呼びたくない理由は、共有リソースをロックする方法の私の理解に問題がないため、FCL静的モニタにいくつかの根本的な問題がある。より良い理解のために、Kernal Mode Syncを使いたいです。 私が解決しようとしている根本的な問題は、LeaveメソッドがEnterより速く呼び出されている理由です。メインスレッドは、leaveにコールする別のスレッドの実行よりも、Lockへの到達がどのように遅くなりますか?> –