2016-06-22 11 views
5

lock verwendet dieses MusterLocking mit Timeout Muster

if(Monitor.Enter(lock)) 
    try 
    { 
     ... 
    } 
    finally { Monitor.Exit(lock); } // using this style to reduce post "height" 

, wenn wir nicht unendlich warten möchten wir Timeout

if(!Monitor.TryEnter(lock, timeout)) 
    throw new TimeoutException(); 
try 
{ 
    ... 
} 
finally { Monitor.Exit(lock); } 

Ich habe Szenario liefern, wenn Verfahren, bevor es mehrere Schlösser erhalten hat fang an, irgendetwas zu tun. Das sieht schrecklich:

if(!Monitor.TryEnter(lockA, timeout)) 
    throw new TimeoutException(); 
try 
{ 
    if(!Monitor.TryEnter(lockB, timeout)) 
     throw new TimeoutException(); 
    try 
    { 
     if(!Monitor.TryEnter(lockC, timeout)) 
      throw new TimeoutException(); 
     try 
     { 
      ... // more of such constructions 
     } 
     finally { Monitor.Exit(lockC); } 
    } 
    finally { Monitor.Exit(lockB); } 
} 
finally { Monitor.Exit(lockA); } 

Es hat Probleme:

  • hässlich aussieht (der Code-Methode ist gegliederte, sich vorstellen, wie es aussieht für lockZ) können, indem sie Methodencode in ein anderes Verfahren gelöst werden .

  • Die Synchronisierung erfolgt synchron, so dass der schlimmste erfolgreiche Fall etwas länger dauern kann als eine Summe aller Timeouts.

Gibt es eine Möglichkeit, dieses Timeout-Muster zu verbessern?

Ich dachte, ein Verfahren mit Delegat Parameter und sperren, um so etwas wie Linq Verkettungs zu erreichen (aber auch Schlösser parallel laufen, ist dies eine Herausforderung):

Lock(lockA).Lock(lockB).Lock(lockC).Run(() => ...); 

Oder vielleicht gibt es einen anderen Weg, ?

Antwort

6

ich mit usings für Synchronisierverriegelung gehen würde:

using System; 
using System.Threading; 

public class MyLock : IDisposable 
{ 
    private object lockObj; 

    public Lock(object lockObj, TimeSpan timeout) 
    { 
     this.lockObj = lockObj; 
     if (!Monitor.TryEnter(this.lockObj, timeout)) 
      throw new TimeoutException(); 
    } 

    public void Dispose() 
    { 
     Monitor.Exit(lockObj); 
    } 
} 

Verbrauch:

using(new MyLock(lockA, new TimeSpan.FromSeconds(1))) 
using(new MyLock(lockB, new TimeSpan.FromSeconds(2))) 
using(new MyLock(lockC, new TimeSpan.FromSeconds(3))) 
{ 
    // your code 
} 

Sie wissen nicht, ob "Sperren" in Ctor gutes Muster/Design ist, aber es wird funktionieren;)

Für asynchrone. Parralisierung ist keine gute Idee. Warum? Wenn ein Thread in den Monitor gelangt, muss er von demselben Thread verlassen werden (Beenden mit Sperre). Also, wenn Sie auf objA innerhalb Parallel.ForEach (f.e.) sperren, werden Sie nicht wissen, welcher Thread es getan hat. Du wirst es also nicht veröffentlichen können.

+1

Sperren in ctor ist definitiv ein gut verbreitetes Muster, vor allem in C++. Es heißt RAII. –