我需要通過字符串鎖定一段代碼。當然下面的代碼是不可靠的:按字符串鎖定。這是安全/理智嗎?
lock("http://someurl")
{
//bla
}
所以我一直在做一個替代方案。我通常不是一個在這裏發佈大量代碼的人,但是當談到併發編程時,我對自己的同步方案有點擔心,所以我提交了我的代碼,詢問是否理智這樣或者是否有更直接的方法。
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;
}
}
}
我會用這樣的:
StringLock.LockOperation("http://someurl",()=>{
//bla
});
好到哪裏去,或和好如初?
編輯
對於後代,這是我的工作代碼。感謝所有的建議:
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;
}
}
}
使用方法如下:
var stringLock=new StringLock();
//...
using(stringLock.AcquireLock(someKey))
{
//bla
}
它看起來過於專注於我,但很難說不知道它應該解決什麼問題。這只是爲了避免鎖定在一個字符串? – LukeH 2010-11-19 12:27:51
@LukeH,我試圖寫一個圖像緩存多個客戶端(超過100左右)可能同時請求圖像。第一次命中將請求來自另一臺服務器的映像,並且我希望緩存中的所有其他請求都被阻止,直到此操作完成,以便它們可以檢索緩存的版本。在我看來,這將需要鎖定這種性質,但如果有其他選擇,我全都是耳朵。 – spender 2010-11-19 12:36:17
@Ani,是的,我知道這一點(正如我在文章中所述),如果我認爲這種方法是一個遊戲者,我會支撐防禦。 – spender 2010-11-19 12:37:11