C# 中线程的文档中存在歧义
在阅读 Joe Albahari 的优秀著作《C# 中的线程》时,我遇到了以下含糊不清的句子:
线程安全类型并不一定使使用它的程序成为线程安全的,并且后者涉及的工作通常会使前者变得多余。
(您可以在本页找到这句话;只需搜索为了“不确定性”快速跳转到适当的部分。)
我希望使用 ConcurrentDictionary 来实现某些线程安全的数据结构。该段落是否告诉我 ConcurrentDictionary 不保证对我的数据结构的线程安全写入?有人可以提供一个反例来显示线程安全类型实际上无法提供线程安全吗?
预先感谢您的帮助。
While reading Joe Albahari's excellent book "Threading in C#" I came across the following ambiguous sentence:
A thread-safe type does not necessarily make the program using it thread-safe, and often the work involved in the latter makes the former redundant.
(You can find the sentence on this page; just search for "indeterminacy" to quickly jump to the appropriate section.)
I am looking to use a ConcurrentDictionary to implement certain thread-safe data structures. Is the paragraph telling me that ConcurrentDictionary does not guarantee thread-safe writes to my data structure? Can someone please provide a counter-example that shows a thread-safe type actually failing to provide thread safety?
Thanks in advance for your help.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
最简单的是,线程安全列表或字典就是一个很好的例子;让每个单独操作线程安全并不总是足够的 - 例如,“检查列表是否为空;如果是,则添加一个项目” - 即使都是线程安全的,你也不能do:
因为两者之间可能会发生变化。您需要同步测试和更改。
At the simplest, a thread safe list or dictionary is a good example; having each individual operation thread safe isn't always enough - for example, "check if the list is empty; if it is, add an item" - even if all thread-safe, you can't do:
as it could change between the two. You need to synchronize the test and the change.
我对警告的理解是,仅仅因为您使用线程安全变量并不意味着您的程序是线程安全的。
作为示例,考虑一个具有两个可以从两个线程修改的变量的类。仅仅因为这些变量单独是线程安全的,并不能保证对类的修改的原子性。如果有两个线程修改这些变量,则一个变量最终可能会由一个线程设置值,而另一个变量则由另一个线程设置。这样很容易破坏类的内部一致性。
My understanding of the warning is that just because you are using thread safe variables does not mean that your program is thread safe.
As an example, consider a class that has two variables that can be modified from two threads. Just because these variables are individually thread safe doesn't guarantee atomicity of modifications to the class. If there are two threads modifying these variables, it is possible that one variable will end up with the value set by one thread, while the other gets set by another thread. This can easily break the internal consistency of the class.
不久前进行了一些搜索以解决我在线程方面遇到的问题并发现了此页面:
http://www.albahari.com/threading/part2.aspx#_Thread_Safety
特别是“锁定线程安全对象”部分
来自页面:
Was doing some searching a while back to fix a problem I had with some threading and came across this page:
http://www.albahari.com/threading/part2.aspx#_Thread_Safety
Particularly the section on "Locking around thread-safe objects"
From the page:
我认为他的意思是,仅仅在任何地方使用 ConcurrentDictionary 而不是 Dictionary 并不会使程序成为线程安全的。因此,如果您有一个非线程安全的程序,搜索和替换不会有帮助;同样,在任何地方添加
SynchronizedAttribute
也不会像魔法仙尘那样起作用。对于集合来说尤其如此,因为迭代始终是一个问题[1]。另一方面,如果将非线程安全的程序重构为更加线程安全的设计,那么通常不需要线程安全的数据结构。一种流行的方法是根据相互发送“消息”的“参与者”重新定义程序——除了单个生产者/消费者风格的消息队列之外,每个参与者都可以独立运行,不需要使用线程安全数据内部结构。
[1] BCL 集合的第一个版本包括一些“线程安全”集合,这些集合在迭代过程中显然不是线程安全的。并发集合在迭代期间是线程安全的,但会与其他线程的修改同时进行迭代。其他集合库允许“快照”,然后可以对其进行迭代,忽略来自其他线程的修改。
I think what he means is that just using
ConcurrentDictionary
instead ofDictionary
everywhere isn't going to make the program thread-safe. So, if you have a non-thread-safe program, a search and replace isn't going to help; likewise, addingSynchronizedAttribute
everywhere isn't going to work like a magic fairy dust. This is particularly true regarding collections, where iteration is always a problem[1].On the other hand, if you restructure the non-thread-safe program into a more thread-safe design, then you often don't need thread-safe data structures. One popular approach is to redefine the program in terms of "actors" that send "messages" to each other - aside from a single producer/consumer-style message queue, each actor can stand alone and does not need to use thread-safe data structures internally.
[1] The first release of BCL collections included some "thread-safe" collections that just plain were not thread-safe during iterations. The Concurrent collections are thread-safe during iteration, but iterate concurrently with other threads' modifications. Other collection libraries allow "snapshots" which can then be iterated, ignoring modifications from other threads.
这是一个有点模糊的陈述,但请考虑例如,一个类有两个成员,每个成员都是线程安全的,但都必须以原子方式更新。
在处理这种情况时,您可能会使整个操作成为原子操作,从而成为线程安全的,从而使对各个成员的线程安全访问变得无关紧要。
如果并不意味着您的 ConcurrentDictionary 将以不安全的方式运行。
It's a bit of a vague statement, but consider for example, a class has two members, each of which is thread-safe, but that must both be updated in an atomic manner.
In dealing with that situation, you're likely to make that entire operation atomic, and thus thread-safe, rendering the thread-safe access to the individual members irrelevant.
If doesn't mean that your ConcurrentDictionary is going to behave in an unsafe way.
我的简要解释是这样的。线程安全有多种形式,满足一种形式的代码不会自动满足所有其他形式。
My concise explanation is this. There are many forms of thread safety and code that satisfies one form does not automatically satisfy all the others.
罗伊,
我猜你“过度阅读”了一个过于简洁的句子......我将这句话解释为两件事:
周期:多线程非常困难,使用适当的开箱即用的数据结构是任何解决方案的重要组成部分,但它肯定不是整个解决方案......除非你'重新准备想清楚(即做你的同步作业)你只是在自欺欺人地说数据结构会以某种方式神奇地“修复”你的程序
我知道这听起来“有点刺耳”,但我的看法是很多。 ,他们真的很失望?!?!
当菜鸟发现编程(在这个动画图标和 GUI 画家的开明时代)需要深入思考时
Roy,
I guess you're "over-reading" a too-concise sentence... I interpret that sentence as meaning two things:
Ergo: Multi-threading is pretty hard, using appropriate out-of-the-box datastructures is an important part of any solution, but it's certainly NOT the whole solution... and unless you're prepared to think-it-through (i.e. do your syncronization home-work) you're just kidding yourself that a data-structure will somehow magically "fix" your program.
I know that sounds "a bit harsh" but my perception is that a lot of noobs are really disappointed when they discover that programming (still, in this enlightened age of animiated icons and GUI painters) requires Deep Thought. Who'd've thunk it?!?!
Cheers. Keith.
不,乔·阿尔巴哈里不是这个意思。
ConcurrentDictionary
将始终通过多个线程的同时写入保持一致的状态。另一个线程永远不会看到处于不一致状态的数据结构。但是,在多线程环境中,线程安全类型的一系列读取和写入仍然可能失败。
显然,ConcurrentQueue 是线程安全类型,但如果另一个线程将
IsEmpty
之间的最后一个项目出队,则该程序仍可能失败并出现NullReferenceException
和 TryDequeue 方法。数据结构本身仍然通过保持一致状态来提供线程安全保证,但程序不是线程安全的,因为它对线程安全的假设通常是不正确的。在这种情况下,程序是不正确的;不是数据结构。No, that is not what Joe Albahari means.
ConcurrentDictionary
will always maintain a consistent state through simultaneous writes from multiple threads. Another thread will never see the data structure in an inconsistent state.However, a series of reads and writes from a thread-safe type may still fail in a multithreaded environment.
So clearly
ConcurrentQueue
is a thread-safe type, but this program can still fail with aNullReferenceException
if another thread dequeued the last item between theIsEmpty
andTryDequeue
methods. The data structure itself still provides its thread-safety guarentee by remaining in a consistent state, but the program is not thread-safe by assumptions it make about thread-safety in a general are not correct. In this case its the program that is incorrect; not the data structure.