2009-10-12 65 views

回答

5

线程安全的类型可以安全地从多个线程访问,而不必考虑并发性。这通常意味着该类型是只读的。

有趣的是,Queue<T>不是线程安全 - 只要队列没有被修改,但它可以支持并发读取,但与线程安全不同。

为了考虑线程安全性,考虑如果两个线程访问一个Queue<T>会发生什么情况,第三个线程出现并开始添加或删除此Queue<T>。由于此类型不限制此行为,因此它不是线程安全的。

25

“线程安全”有点不幸,因为它没有真正的定义。基本上这意味着对象的某些操作在通过多线程操作对象时保证合理运行。

考虑最简单的例子:一个计数器。假设你有两个正在增加计数器的线程。如果事件顺序如下:

  • 线程1从计数器读取,得到 为零。
  • 线程从计数器读取两次,得到 为零。
  • 线程一递增零,写入 一个来反击。
  • 线程两个增量零,写入 一个来反击。

然后注意计数器是如何“失去”其中一个增量的。计数器上的简单增量操作不是线程安全的;让它们成为线程安全的,你可以使用锁或InterlockedIncrement。

与队列类似。非线程安全队列可能会“失去”排队的方式与非线程安全计数器可能会丢失增量的方式相同。更糟糕的是,如果您在多线程场景中不正确地使用它们,线程安全队列甚至可能会崩溃或产生疯狂的结果。

“线程安全”的难点在于它没有明确定义。它是否意味着“不会崩溃”?这是否意味着会产生明智的结果?例如,假设你有一个“线程安全”的集合。这段代码是否正确?

if (!collection.IsEmpty) Console.WriteLine(collection[0]); 

不,即使集合是“线程安全的”,这并不意味着这段代码是正确的;另一个线程可能会在收集之后将收集留空,但在写入线之前,因此此代码可能会崩溃,即使该对象被称为“线程安全”。实际上确定操作的每个相关组合都是线程安全的,这是一个极其困难的问题。

现在来谈谈你的实际情况:任何告诉你“你应该使用Queue类,更好,因为它是线程安全的”的人可能并不清楚他们在说什么。首先,队列不是线程安全的。其次,如果仅在单个线程上使用该对象,则队列是否为线程安全是完全不相关的!如果您有一个将在多个线程中访问的集合,那么,正如我在上面的示例中所指出的那样,无论集合本身是否是“线程安全”,都有一个极其困难的问题需要解决。您必须确定您在集合上执行的每个操作组合都是线程安全的。这是一个非常困难的问题,如果它是你所面对的问题,那么你应该使用专家对这个难题的服务。

+5

+1对于坚实的专家证词。你也指出“线程安全”是一个相当模糊的概念。 – 2009-10-12 04:13:57

+1

我喜欢每个线程安全相关的问题都由同一位老兄回答,我现在已经阅读了大约2个小时。 – 2015-11-24 09:36:32

1

在处理多线程时,您通常必须处理并发问题。术语“并发问题”是指由两个不同的执行上下文中的指令交叉存储在两个资源共享的资源上而特别引入的问题。这里,就线程安全而言,执行上下文是一个进程中的两个线程;然而,在相关主题中,他们可能是流程。

线程安全措施的实施主要是为了实现两个目标。首先是重新确定关于如果线程上下文切换(否则由操作系统控制,因此在用户级程序中基本上不确定),防止某些任务留下半完成或两个上下文写入一个接一个地在内存中的相同位置。大多数度量仅仅使用一点硬件支持的指令等,以及软件级别synchronization constructs来强制所有其他执行上下文远离数据类型,而另一个执行上下文正在做不应该中断的工作。

通常,只读对象是线程安全的。如果对象没有在中间修改,那么许多不是只读的对象都可以在多线程中发生数据访问(只读),而不会出现问题。但这不是线程安全。线程安全性是指将所有方式的数据类型都完成为数据类型,以防止一个线程对其进行任何修改,即使在处理多个并发读写时也会导致数据损坏或死锁。