我在C#集合中使用了通用队列,每个人都说最好使用System.Collection.Generic.Queue的对象,因为线程安全。
请告知正确使用Queue对象的决定,以及它是如何安全的?
答案 0 :(得分:26)
“线程安全”是一个不幸的术语,因为它没有真正的定义。基本上,这意味着当通过多个线程操作对象时,保证对象上的某些操作能够合理地运行。
考虑最简单的例子:一个计数器。假设您有两个递增计数器的线程。如果事件顺序如下:
然后注意计数器如何“丢失”其中一个增量。计数器上的简单增量操作不是线程安全的;为了使它们成为线程安全的,你可以使用锁或InterlockedIncrement。
与队列类似。非线程安全队列可以“丢失”排队,就像非线程安全计数器失去增量一样。更糟糕的是,如果您在多线程场景中使用它们不正确,那么线程安全队列甚至不会崩溃或产生疯狂的结果。
“线程安全”的难点在于它没有明确定义。它只是意味着“不会崩溃”吗?这是否意味着会产生明智的结果?例如,假设您有一个“threadsafe”集合。这段代码是否正确?
if (!collection.IsEmpty) Console.WriteLine(collection[0]);
没有。即使集合是“线程安全的”,也不意味着这个代码是正确的;另一个线程可能在检查之后但在写入线之前使集合变空,因此即使对象被称为“线程安全”,此代码也可能崩溃。实际上确定操作的每个相关组合都是线程安全的,这是一个非常困难的问题。
现在来看看你的实际情况:任何告诉你“你应该使用Queue类,它更好,因为它是线程安全”的人可能并不清楚他们在谈论什么。首先,Queue不是线程安全的。其次,如果你只在一个线程上使用该对象,那么Queue是否是线程安全是完全无关紧要的!如果你有一个将在多个线程上访问的集合,那么,正如我在上面的例子中指出的那样,无论集合本身是否是“线程安全”,你都有一个非常难以解决的问题。您必须确定您对集合执行的操作的每个组合也是线程安全的。这是一个非常困难的问题,如果它是你所面临的问题,那么你应该在这个困难的话题上使用专家的服务。
答案 1 :(得分:5)
可以从多个线程安全地访问线程安全的类型,而无需考虑并发性。这通常意味着该类型是只读的。
有趣的是,Queue<T>
不线程安全 - 只要队列未被修改但它与线程安全性不同,它就可以支持并发读取。 / p>
为了考虑线程安全性,考虑如果两个线程正在访问Queue<T>
并且第三个线程出现并开始添加或删除此Queue<T>
会发生什么。由于此类型不限制此行为,因此它不是线程安全的。
答案 2 :(得分:1)
在处理多线程时,您通常必须处理并发问题。术语“并发问题”指的是由两个不同的执行上下文交织指令的可能性而特别引入的问题。这里,就线程安全而言,执行上下文是进程中的两个线程;但是,在相关主题中,它们可能是过程。
实施线程安全措施主要实现两个目标。首先是重新确定如果线程上下文切换(由操作系统控制,因此在用户级程序中基本上不确定)发生的事情的确定性,以防止某些任务被半完成或两个上下文写入一个接一个地在内存中的相同位置。大多数度量只是使用一些硬件支持的test-and-set指令等,以及软件级synchronization constructs来强制所有其他执行上下文远离数据类型而另一个正在执行不应该被打断的工作。
通常,只读对象是线程安全的。如果对象未在中间修改,则许多不是只读的对象能够在没有问题的情况下对多个线程进行数据访问(只读)。但这不是线程安全。线程安全是指对数据类型执行所有方式以防止一个线程对其进行任何修改,即使在处理许多并发读取和写入时也会导致数据损坏或死锁。