使用字符串作为锁来做线程同步
当我查看一些遗留应用程序代码时,我注意到它正在使用字符串对象来进行线程同步。我正在尝试解决该程序中的一些线程争用问题,并且想知道这是否会导致一些奇怪的情况。有什么想法吗?
private static string mutex= "ABC";
internal static void Foo(Rpc rpc)
{
lock (mutex)
{
//do something
}
}
While i was looking at some legacy application code i noticed it is using a string object to do thread synchronization. I'm trying to resolve some thread contention issues in this program and was wondering if this could lead so some strange situations. Any thoughts ?
private static string mutex= "ABC";
internal static void Foo(Rpc rpc)
{
lock (mutex)
{
//do something
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
像这样的字符串(来自代码)可能是“interned”。这意味着“ABC”的所有实例都指向同一个对象。即使跨 AppDomain,您也可以指向同一个对象(谢谢史蒂文的提示)。
如果您有很多来自不同位置但具有相同文本的字符串互斥锁,那么它们都可以锁定同一个对象。
最好使用:
此外,由于您的字符串不是 const 或 readonly,因此您可以更改它。因此(理论上)可以锁定您的互斥体。将互斥锁更改为另一个引用,然后进入临界区,因为锁使用了另一个对象/引用。例子:
Strings like that (from the code) could be "interned". This means all instances of "ABC" point to the same object. Even across AppDomains you can point to the same object (thx Steven for the tip).
If you have a lot of string-mutexes, from different locations, but with the same text, they could all lock on the same object.
It's better to use:
Also, since your string is not
const
orreadonly
, you can change it. So (in theory) it is possible to lock on yourmutex
. Changemutex
to another reference, and then enter a critical section because the lock uses another object/reference. Example:为了回答您的问题(正如其他一些人已经提出的那样),您提供的代码示例存在一些潜在的问题:
mutex
不是不可变的。"ABC"
将在应用程序中的各处引用相同的内部对象引用。一般来说,我建议不要锁定字符串。然而,我遇到过一个案例,这样做很有用。
有时我会维护一个锁对象字典,其中的键是我拥有的某些数据的唯一值。这是一个人为的示例:
这样的代码的目标是在实体的
Id
上下文中序列化DoSomething()
的并发调用。缺点是字典。实体越多,它就越大。这也只是需要阅读和思考更多的代码。我认为 .NET 的字符串驻留可以简化事情:
这里的区别是我依靠字符串驻留为每个实体 id 提供相同的对象引用。这简化了我的代码,因为我不必维护互斥体实例的字典。
请注意我用作命名空间的硬编码 UUID 字符串。如果我选择在应用程序的另一个区域采用相同的锁定字符串的方法,这一点很重要。
锁定字符串可能是一个好主意,也可能是一个坏主意,具体取决于具体情况以及开发人员对细节的关注程度。
To answer your question (as some others already have), there are some potential problems with the code example you provided:
mutex
is not immutable."ABC"
will refer to the same interned object reference everywhere in your application.In general, I would advise against locking on strings. However, there is a case I've ran into where it is useful to do this.
There have been occasions where I have maintained a dictionary of lock objects where the key is something unique about some data that I have. Here's a contrived example:
The goal of code like this is to serialize concurrent invocations of
DoSomething()
within the context of the entity'sId
. The downside is the dictionary. The more entities there are, the larger it gets. It's also just more code to read and think about.I think .NET's string interning can simplify things:
The difference here is that I am relying on the string interning to give me the same object reference per entity id. This simplifies my code because I don't have to maintain the dictionary of mutex instances.
Notice the hard-coded UUID string that I'm using as a namespace. This is important if I choose to adopt the same approach of locking on strings in another area of my application.
Locking on strings can be a good idea or a bad idea depending on the circumstances and the attention that the developer gives to the details.
我的 2 美分:
ConcurrentDictionary
比实习字符串快 1.5 倍。我做了一次基准测试。要解决“不断增长的字典”问题,您可以使用信号量字典而不是对象字典。 AKA 使用
ConcurrentDictionary
而不是
。与lock语句不同,信号量可以跟踪有多少线程锁定了它们。一旦所有的锁被释放 - 您可以将其从字典中删除。请参阅此问题以获取类似的解决方案:基于密钥的异步锁定信号量甚至更好,因为您甚至可以控制并发级别。就像,您可以“限制为 5 个并发运行”,而不是“限制为 1 个并发运行”。很棒的免费奖金不是吗?我必须编写一个电子邮件服务,该服务需要限制与服务器的并发连接数 - 这非常方便。
My 2 cents:
ConcurrentDictionary
is 1.5X faster than interned strings. I did a benchmark once.To solve the "ever-growing dictionary" problem you can use a dictionary of semaphores instead of a dictionary of objects. AKA use
ConcurrentDictionary<string, SemaphoreSlim>
instead of<string, object>
. Unlike thelock
statements, Semaphores can track how many threads have locked on them. And once all the locks are released - you can remove it from the dictionary. See this question for solutions like that: Asynchronous locking based on a keySemaphores are even better because you can even control the concurrency level. Like, instead of "limiting to one concurrent run" - you can "limit to 5 concurrent runs". Awesome free bonus isn't it? I had to code an email-service that needed to limit the number of concurrent connections to a server - this came very very handy.
如果需要锁定字符串,您可以创建一个对象,将字符串与可锁定的对象配对。
If you need to lock a string, you can create an object that pairs the string with an object that you can lock with.
我想如果生成的字符串很多并且都是唯一的,那么锁定驻留字符串可能会导致内存膨胀。另一种应该提高内存效率并解决直接死锁问题的方法是
I imagine that locking on interned strings could lead to memory bloat if the strings generated are many and are all unique. Another approach that should be more memory efficient and solve the immediate deadlock issue is