在阅读Joe Albahari的优秀着作“C#中的线程”时,我遇到了以下含糊不清的句子:
线程安全类型并不一定使程序使用它是线程安全的,并且后者所涉及的工作通常会使前者冗余。
(您可以在 this page 上找到句子;只需搜索“不确定性”即可快速跳转到相应的部分。)
我希望使用ConcurrentDictionary来实现某些线程安全的数据结构。段落告诉我ConcurrentDictionary不保证对我的数据结构的线程安全写入吗?有人可以提供一个反例,显示线程安全类型实际上无法提供线程安全吗?
提前感谢您的帮助。
答案 0 :(得分:8)
最简单的说,线程安全列表或字典就是一个很好的例子;让每个个人操作线程安全并不足够 - 例如,"检查列表是否为空;如果是的话,添加一个项目" - 即使所有线程都安全,您也无法做到:
if(list.Count == 0) list.Add(foo);
因为它可能会在两者之间发生变化。您需要同步测试 和 更改。
答案 1 :(得分:4)
我对警告的理解是因为你使用线程安全变量并不意味着你的程序是线程安全的。
举个例子,考虑一个有两个可以从两个线程修改的变量的类。仅仅因为这些变量是单独的线程安全的,并不能保证对类的修改的原子性。如果有两个线程修改这些变量,则一个变量最终可能由一个线程设置的值,而另一个变量由另一个线程设置。这很容易打破班级的内部一致性。
答案 2 :(得分:4)
我正在做一些搜索以解决我遇到的一些线程问题,并且遇到了这个页面:
http://www.albahari.com/threading/part2.aspx#_Thread_Safety
特别是关于“锁定线程安全对象”的部分
从页面:
有时您还需要锁定访问线程安全对象。为了说明,假设Framework的List类确实是线程安全的,我们想要将一个项添加到列表中:
if (!_list.Contains (newItem)) _list.Add (newItem);
列表是否是线程安全的,这个陈述肯定不是!
答案 3 :(得分:2)
我认为他的意思是在任何地方使用ConcurrentDictionary
代替Dictionary
都不会使程序成为线程安全的。因此,如果您有一个非线程安全的程序,搜索和替换将无济于事;同样地,在任何地方添加SynchronizedAttribute
都不会像魔法般的尘埃一样起作用。对于集合来说尤其如此,其中迭代始终是一个问题[1]。
另一方面,如果将非线程安全程序重组为更加线程安全的设计,那么通常不需要线程安全的数据结构。一种流行的方法是根据向彼此发送“消息”的“参与者”重新定义程序 - 除了单个生产者/消费者风格的消息队列之外,每个参与者可以独立存在而不需要使用线程安全的数据内部结构。
[1] BCL集合的第一个版本包括一些“线程安全”集合,这些集合在迭代期间只是不线程安全。并发集合在迭代期间是线程安全的,但与其他线程的修改同时迭代。其他集合库允许“快照”,然后可以迭代,忽略来自其他线程的修改。
答案 4 :(得分:1)
这是一个模糊的陈述,但例如,考虑一个类有两个成员,每个成员都是线程安全的,但必须以原子方式更新。
在处理这种情况时,您可能会使整个操作成为原子操作,从而保证线程安全,从而使对各个成员的线程安全访问变得无关紧要。
如果并不意味着您的ConcurrentDictionary将以不安全的方式运行。
答案 5 :(得分:1)
我的简要解释是这样的。有许多形式的线程安全和代码满足一种形式不会自动满足所有其他形式。
答案 6 :(得分:0)
罗伊,
我猜你是“过度阅读”一个过于简洁的句子......我将这句话解释为两件事:
Ergo:多线程非常困难,使用适当的开箱即用数据结构是任何解决方案的重要组成部分,但它肯定不是整个解决方案......除非你准备好思考它通过(即你的同步家庭工作)你只是在开玩笑说数据结构会以某种方式神奇地“修复”你的程序。
我知道这听起来“有点刺耳”,但我的看法是,当他们发现编程(仍然是在这个开明的图标和GUI画家的开明时代)需要深思熟虑时,许多新手真的很失望。谁会把它弄糟?!?!
干杯。基思。
答案 7 :(得分:0)
段落是否告诉我 ConcurrentDictionary没有 保证线程安全写入我的 数据结构?
不,这不是Joe Albahari的意思。 ConcurrentDictionary
将始终通过多个线程的同时写入保持一致状态。另一个线程永远不会看到数据结构处于不一致状态。
有人可以提供一个 反例显示了一个 线程安全类型实际上没有 提供线程安全吗?
但是,在多线程环境中,线程安全类型的一系列读取和写入仍可能失败。
void ExecutedByMultipleThreads(ConcurrentQueue<object> queue)
{
object value;
if (!queue.IsEmpty)
{
queue.TryDequeue(out value);
Console.WriteLine(value.GetHashCode());
}
}
很明显ConcurrentQueue
是一种线程安全的类型,但是如果另一个线程将NullReferenceException
和IsEmpty
之间的最后一个项目队列化,则此程序仍可能失败TryDequeue
方法。数据结构本身仍然通过保持一致的状态来提供其线程安全保护,但是程序不是线程安全的,因为它假设一般的线程安全性是不正确的。在这种情况下,程序是不正确的;不是数据结构。