2016-06-22 31 views
5

lock utilizza questo schemabloccaggio con il modello timeout

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

se non vogliamo aspettare all'infinito possiamo fornire timeout

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

ho scenario in cui il metodo deve ottenere serrature multiple prima di esso inizia a fare qualsiasi cosa Questo sembra terribile:

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); } 

Ha problemi:

  • sembra brutto (il codice del metodo è rientrato, immaginare come sarà sguardi per lockZ), può essere risolto mettendo codice del metodo in un altro metodo .

  • il blocco si verifica in modo sincrono, quindi il caso peggiore potrebbe richiedere del tempo leggermente inferiore alla somma di tutti i timeout.

C'è un modo per migliorare questo schema di timeout?

Stavo pensando di fare un metodo con il parametro delegato e di blocco per realizzare qualcosa di simile LINQ concatenamento (ma per eseguire anche le serrature in parallelo, questa è una sfida):

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

O forse c'è un altro modo ?

risposta

6

vorrei andare con usings per blocco di sincronizzazione:

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); 
    } 
} 

Usage:

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 
} 

non so se "blocco" in ctor è buon modello/disegno, ma funzionerà;)

Per async. Il parralization non è una buona idea. Perché? Se alcuni thread entrano nel monitor, lo stesso thread deve lasciarlo (uscire con il lucchetto). Quindi se si blocca objA all'interno di Parallel.ForEach (f.e.) non si saprà quale thread lo ha fatto. Quindi non sarai in grado di rilasciarlo.

+1

Il blocco di Ctor è sicuramente uno schema ben distribuito, specialmente in C++. Si chiama RAII. –