Ho bisogno di bloccare una sezione di codice per stringa. Ovviamente il seguente codice è orribilmente pericoloso:Blocco per stringa. È sicuro/sano?
lock("http://someurl")
{
//bla
}
Quindi ho cucinato un'alternativa. Di solito non sono io a pubblicare grandi quantità di codice, ma quando si tratta di programmazione simultanea, sono un po 'preoccupato di creare il mio schema di sincronizzazione, quindi sto inviando il mio codice per chiedere se è sano farlo in questo modo o se c'è un approccio più diretto.
public class StringLock
{
private readonly Dictionary<string, LockObject> keyLocks = new Dictionary<string, LockObject>();
private readonly object keyLocksLock = new object();
public void LockOperation(string url, Action action)
{
LockObject obj;
lock (keyLocksLock)
{
if (!keyLocks.TryGetValue(url,
out obj))
{
keyLocks[url] = obj = new LockObject();
}
obj.Withdraw();
}
Monitor.Enter(obj);
try
{
action();
}
finally
{
lock (keyLocksLock)
{
if (obj.Return())
{
keyLocks.Remove(url);
}
Monitor.Exit(obj);
}
}
}
private class LockObject
{
private int leaseCount;
public void Withdraw()
{
Interlocked.Increment(ref leaseCount);
}
public bool Return()
{
return Interlocked.Decrement(ref leaseCount) == 0;
}
}
}
vorrei utilizzare in questo modo:
StringLock.LockOperation("http://someurl",()=>{
//bla
});
bene ad andare, o crash e bruciare?
EDIT
Ai posteri, ecco il mio codice di lavoro. Grazie per tutti i suggerimenti:
public class StringLock
{
private readonly Dictionary<string, LockObject> keyLocks = new Dictionary<string, LockObject>();
private readonly object keyLocksLock = new object();
public IDisposable AcquireLock(string key)
{
LockObject obj;
lock (keyLocksLock)
{
if (!keyLocks.TryGetValue(key,
out obj))
{
keyLocks[key] = obj = new LockObject(key);
}
obj.Withdraw();
}
Monitor.Enter(obj);
return new DisposableToken(this,
obj);
}
private void ReturnLock(DisposableToken disposableLock)
{
var obj = disposableLock.LockObject;
lock (keyLocksLock)
{
if (obj.Return())
{
keyLocks.Remove(obj.Key);
}
Monitor.Exit(obj);
}
}
private class DisposableToken : IDisposable
{
private readonly LockObject lockObject;
private readonly StringLock stringLock;
private bool disposed;
public DisposableToken(StringLock stringLock, LockObject lockObject)
{
this.stringLock = stringLock;
this.lockObject = lockObject;
}
public LockObject LockObject
{
get
{
return lockObject;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~DisposableToken()
{
Dispose(false);
}
private void Dispose(bool disposing)
{
if (disposing && !disposed)
{
stringLock.ReturnLock(this);
disposed = true;
}
}
}
private class LockObject
{
private readonly string key;
private int leaseCount;
public LockObject(string key)
{
this.key = key;
}
public string Key
{
get
{
return key;
}
}
public void Withdraw()
{
Interlocked.Increment(ref leaseCount);
}
public bool Return()
{
return Interlocked.Decrement(ref leaseCount) == 0;
}
}
}
utilizzati come segue:
var stringLock=new StringLock();
//...
using(stringLock.AcquireLock(someKey))
{
//bla
}
Sembra troppo ingegnoso per me, ma difficile da dire senza sapere quale problema dovrebbe risolvere. È giusto per evitare di bloccarsi su una corda? – LukeH
@LukeH, Sto cercando di scrivere una cache di immagini in cui più client (più di 100) richiedono l'immagine simultaneamente. Il primo hit richiederà l'immagine da un altro server e mi piacerebbe che tutte le altre richieste alla cache venissero bloccate fino a quando l'operazione non sarà completa, in modo che possano recuperare la versione cache. A mio avviso, ciò richiederà il blocco di questa natura, ma se ci sono alternative, sono tutto orecchie. – spender
@Ani, sì, sono a conoscenza di questo (come descritto nel mio post) e se considero questo approccio come un frequentatore, farò da barriera alle difese. – spender