通过字符串锁定.这安全吗? [英] Locking by string. Is this safe/sane?
问题描述
我需要按字符串锁定一段代码.当然,以下代码非常不安全:
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
});
好走,还是撞毁并烧毁?</p>
编辑
为后代,这是我的工作代码.感谢您的所有建议:
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
}
另一个选择是获取每个URL的HashCode,然后将其除以质数,然后将其用作锁数组的索引.这样可以限制所需的锁数量,同时可以通过选择要使用的锁数量来控制错误锁定"的可能性.
但是,上述方法仅在成本太高的情况下才值得,每个活动网址只有一个锁.
I need to lock a section of code by string. Of course the following code is hideously unsafe:
lock("http://someurl")
{
//bla
}
So I've been cooking up an alternative. I'm not normally one to post large bodies of code here, but when it comes to concurrent programming, I'm a little apprehensive about making my own synchronization scheme, so I'm submitting my code to ask if it's sane to do it in this way or whether there's a more straightforward approach.
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;
}
}
}
I would use it like this:
StringLock.LockOperation("http://someurl",()=>{
//bla
});
Good to go, or crash and burn?
EDIT
For posterity, here's my working code. Thanks for all the suggestions:
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;
}
}
}
Used as follows:
var stringLock=new StringLock();
//...
using(stringLock.AcquireLock(someKey))
{
//bla
}
Another option is to get the HashCode of each URL, then divide it by a prime number and use it as an index into an array of locks. This will limit the number of locks you need while letting you control the probability of a "false locking" by choose the number of locks to use.
However the above is only worthwhile if it is too costly just have one lock per active url.
这篇关于通过字符串锁定.这安全吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!