可以使用string作为锁对象吗?
我需要在一个有限的string的基础上在一个地区做一个关键的部分。 我想要锁被共享相同的string实例,(有点类似于String.Intern方法)。
我正在考虑以下实施:
public class Foo { private readonly string _s; private static readonly HashSet<string> _locks = new HashSet<string>(); public Foo(string s) { _s = s; _locks.Add(s); } public void LockMethod() { lock(_locks.Single(l => l == _s)) { ... } } }
这种方法有什么问题吗? 以这种方式lockingstring对象是否可行,并且在使用HashSet<string>
是否存在线程安全问题?
例如,创build一个为每个string实例创build一个新的locking对象的Dictionary<string, object>
会更好吗?
最终实施
根据我的build议,进行了以下的实施:
public class Foo { private readonly string _s; private static readonly ConcurrentDictionary<string, object> _locks = new ConcurrentDictionary<string, object>(); public Foo(string s) { _s = s; } public void LockMethod() { lock(_locks.GetOrAdd(_s, s => new object())) { ... } } }
不鼓励对string进行locking,主要原因是(因为string实现)一些其他的代码可能会locking在相同的string实例上,而不知道这一点。 创造一个潜在的死锁情况。
现在在大多数具体的情况下,这可能是一个很遥不可及的场景。 这对图书馆来说更为通用。
但另一方面,string的好处是什么?
所以,指出一点:
这种方法有什么问题吗?
是的,但主要是理论上的。
以这种方式locking一个string对象是否可行,并且在使用HashSet时是否存在线程安全问题?
只要线程只能同时读取, HashSet<>
就不涉及线程安全。
例如,创build一个为每个string实例创build一个新的locking对象的Dictionary是否更好?
是。 只是为了安全起见。 在一个大系统中,避免死锁的主要目的是尽可能保持锁对象的本地和私有。 只有有限的代码应该能够访问它们。
我个人认为这是一个非常糟糕的主意。 这不是什么string。
(就我个人而言,我不喜欢每个物体都有显示器的事实,但这是一个稍微不同的问题。)
如果你想要一个对象代表一个可以在不同实例之间共享的锁,为什么不为它创build一个特定的types呢? 为了诊断的目的,你可以很容易地给locking一个名字,但是locking实际上不是string的目的。 像这样的东西:
public sealed class Lock { private readonly string name; public string Name { get { return name; } } public Lock(string name) { if (name == null) { throw new ArgumentNullException("name"); } this.name = name; } }
考虑到string有时是被实施的方式, 有时候不是这样(偶尔通过简单的检查就难以辨别),那么你可能会很容易地意外地共享那些你不想要的锁。
lockingstring可能会有问题,因为internedstring本质上是全局的。
Internedstring是每个进程,所以它们甚至在不同的AppDomain之间共享。 types对象也是一样的(所以不要lockingtypeof(x))。
不久前,我遇到了类似的问题,我正在寻找一种基于string值locking代码段的好方法。 这是我们目前所处的位置,它解决了string间的问题,并具有我们想要的粒度。
主要思想是使用string键维护同步对象的静态ConcurrentDictionary 。 当一个线程进入方法时,它立即build立一个锁,并尝试将同步对象添加到并发字典中。 如果我们可以添加到并发字典中,这意味着没有其他线程根据我们的string键有锁,我们可以继续我们的工作。 否则,我们将使用并发字典中的同步对象来build立第二个锁,它将等待正在运行的线程完成处理。 当第二个锁被释放时,我们可以尝试再次将当前线程的同步对象添加到字典中。
谨慎的一句话是:线程不会排队,所以如果多个具有相同string键的线程同时竞争一个锁,那么就不能保证它们被处理的顺序。
随意批评,如果你认为我忽略了一些东西。
public class Foo { private static ConcurrentDictionary<string, object> _lockDictionary = new ConcurrentDictionary<string, object>(); public void DoSomethingThreadCriticalByString(string lockString) { object thisThreadSyncObject = new object(); lock (thisThreadSyncObject) { try { for (; ; ) { object runningThreadSyncObject = _lockDictionary.GetOrAdd(lockString, thisThreadSyncObject); if (runningThreadSyncObject == thisThreadSyncObject) break; lock (runningThreadSyncObject) { // Wait for the currently processing thread to finish and try inserting into the dictionary again. } } // Do your work here. } finally { // Remove the key from the lock dictionary object dummy; _lockDictionary.TryRemove(lockString, out dummy); } } } }