2015-05-28 12 views
5

ho il seguente scenario:migliorata consiglio di bloccaggio Discussione necessaria

Sto cercando di bloccare un filo in atto, se le discussioni che 'personalizzato' id corrisponde a quello che è già entrato nella sezione bloccata al largo di codice, ma non se l'ID è diverso.

ho creato un po 'di codice di esempio per spiegare il comportamento che voglio

class A 
{   
    private static Dictionary<int, object> _idLocks = new Dictionary<int, object>(); 
    private static readonly object _DictionaryLock = new object(); 
    private int _id; 

    private void A (int id) 
    { 
     _id = id; 
    } 

    private object getObject() 
    { 
     lock (_DictionaryLock) 
     { 
      if (!_idLocks.ContainsKey(_id)) 
       _idLocks.Add(_id, new object()); 
     } 
     lock (_idLocks[_id]) 
     { 
      if (TestObject.Exists(_id)) 
       return TestObject(_id); 
      else 
       return CreateTestObject(_id); 
     } 
    } 
} 

Ora questo funziona al 100% per quello che ho prolungato, dove id esempio 1 non verifica per vedere se il suo oggetto è stato creato mentre un altro thread con ID 1 è già occupato per la creazione di quell'oggetto.

Ma avere due blocchi e un dizionario statico non sembra un modo corretto di farlo, quindi spero che qualcuno possa mostrarmi un metodo migliorato per fermare un thread dall'accesso al codice solo se quel thread è stato creato con il lo stesso ID di quello già occupato nell'esecuzione del codice nella sezione bloccata.

Stavo guardando la classe ReaderWriterLockSlim ma per me non aveva senso essere usato perché non voglio che l'oggetto TestObject (id) venga letto mentre è ancora in fase di creazione.

Non mi interessa il blocco del thread dall'accesso a un dizionario. Quello che sto cercando di evitare a tutti i costi è l'_id in cui quel thread viene eseguito non deve essere usato all'interno di CreateTestObject(_id) mentre ce n'è già uno occupato, perché i file vengono creati e cancellati con quell'id che genera eccezioni se due thread stanno provando per accedere agli stessi file

Quale è risolvibile con un semplice blocco, ma in questo caso voglio ancora un thread il cui _id non è attualmente in esecuzione all'interno del metodo CreateTestObject(_id) per poter inserire il codice all'interno del blocco.

Questo è tutto perché ciò che accade in CreateTestObject richiede tempo e le prestazioni subiranno un impatto se un thread è in attesa di accedervi.

+0

È possibile eseguire un blocco sull'intero dizionario '_idLocks' anziché sui 2 blocchi? Sono abbastanza nuovo per le serrature quindi non sono sicuro di cosa si possa usare come oggetto di blocco o meno. –

risposta

3

Sembra che tu stia utilizzando questo codice per popolare un dizionario in modo sicuro per i thread: potresti utilizzare uno ConcurrentDictionary?

class A { 
    private static ConcurrentDictionary<int, object> _dictionary = new ConcurrentDictionary<int, object>(); 

    private int _id; 

    private object GetObject() { 
    object output = null; 
    if(_dictionary.TryGetValue(_id, output)) { 
     return output; 
    } else { 
     return _dictionary.GetOrAdd(_id, CreateTestObject(_id)); 
    } 
    } 
} 

Edit: Se si desidera eliminare completamente la possibilità di invocare duplicare CreateTestObject metodi allora si può memorizzare un involucro in _dictionary che imposta pigramente object

class Wrapper { 
    private volatile object _obj = null; 

    public object GetObj() { 
    while(_obj == null) { 
     // spin, or sleep, or whatever 
    } 
    return _obj; 
    } 

    public void SetObj(object obj) { 
    _obj = obj; 
    } 
} 

class A { 
    private static ConcurrentDictionary<int, Wrapper> _dictionary = new ConcurrentDictionary<int, Wrapper>(); 

    private int _id; 

    private object GetObject() { 
    Wrapper wrapper = null; 
    if(_dictionary.TryGetValue(_id, wrapper)) { 
     return wrapper.GetObj(); 
    } else { 
     Wrapper newWrapper = new Wrapper(); 
     wrapper = _dictionary.GetOrAdd(_id, newWrapper); 
     if(wrapper == newWrapper) { 
     wrapper.SetObj(CreateTestObject(_id)); 
     } 
     return wrapper.GetObj(); 
    } 
    } 
} 

Solo un thread sarà in grado per inserire un nuovo Wrapper in _dictionary allo specificato _id - quel thread inizializzerà l'oggetto all'interno del condizionale wrapper == newWrapper. Wrapper#GetObj giri fino a quando l'oggetto è impostato, questo può essere riscritto per bloccare invece.

+0

Non sapevo che esistesse un "ConcurrentDictionary". +1! –

+0

Ciao, ho aggiornato la mia domanda originale per essere più chiara, grazie per la tua assistenza finora – Domitius

+0

@Domitius Ho aggiornato la mia risposta –

0

Questo non funziona, perché Monitor (che viene utilizzato internamente dall'istruzione lock) è rientrante. Ciò significa che un thread può inserire qualsiasi blocco che già possiede un numero qualsiasi di volte.

Si potrebbe risolvere questo problema utilizzando un Semaphore invece di un Monitor, ma fermarsi per un po 'e ascoltare quello che stai chiedendo - si desidera che il filo per bloccare su un lock di proprietà di questo stesso thread.Come si avvia quella sveglia ? Si sbloccherà per sempre - aspettando che venga rilasciato il lock, mentre è anche quello che contiene lo lock.

O stai solo cercando di gestire l'inizializzazione pigra di qualche oggetto senza dover bloccare tutti gli altri thread? Questo è in realtà abbastanza semplice:

ConcurrentDictionary<int, YourObject> dictionary; 

return dictionary.GetOrAdd(id, i => CreateTestObject(i)); 

noti che CreateTextObject si chiama solo se la chiave non esiste nel dizionario ancora.

+0

Ciao, ho aggiornato la mia domanda originale per essere più chiara, grazie per la tua assistenza fino ad ora – Domitius

Problemi correlati