C# 中线程的文档中存在歧义

发布于 2024-11-17 23:55:10 字数 424 浏览 1 评论 0原文

在阅读 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

口干舌燥 2024-11-24 23:55:10

最简单的是,线程安全列表或字典就是一个很好的例子;让每个单独操作线程安全并不总是足够的 - 例如,“检查列表是否为空;如果是,则添加一个项目” - 即使都是线程安全的,你也不能do:

if(list.Count == 0) list.Add(foo);

因为两者之间可能会发生变化。您需要同步测试更改。

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:

if(list.Count == 0) list.Add(foo);

as it could change between the two. You need to synchronize the test and the change.

So要识趣 2024-11-24 23:55:10

我对警告的理解是,仅仅因为您使用线程安全变量并不意味着您的程序是线程安全的。

作为示例,考虑一个具有两个可以从两个线程修改的变量的类。仅仅因为这些变量单独是线程安全的,并不能保证对类的修改的原子性。如果有两个线程修改这些变量,则一个变量最终可能会由一个线程设置值,而另一个变量则由另一个线程设置。这样很容易破坏类的内部一致性。

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.

沦落红尘 2024-11-24 23:55:10

不久前进行了一些搜索以解决我在线程方面遇到的问题并发现了此页面:

http://www.albahari.com/threading/part2.aspx#_Thread_Safety

特别是“锁定线程安全对象”部分

来自页面:

有时您还需要锁定访问线程安全对象。为了说明这一点,假设框架的 List 类确实是线程安全的,并且我们想要将一个项目添加到列表中:

if (!_list.Contains (newItem)) _list.Add (newItem);

无论列表是否是线程安全的,这个声明肯定不是!

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:

Sometimes you also need to lock around accessing thread-safe objects. To illustrate, imagine that the Framework’s List class was, indeed, thread-safe, and we want to add an item to a list:

if (!_list.Contains (newItem)) _list.Add (newItem);

Whether or not the list was thread-safe, this statement is certainly not!

临走之时 2024-11-24 23:55:10

我认为他的意思是,仅仅在任何地方使用 ConcurrentDictionary 而不是 Dictionary 并不会使程序成为线程安全的。因此,如果您有一个非线程安全的程序,搜索和替换不会有帮助;同样,在任何地方添加 SynchronizedAttribute 也不会像魔法仙尘那样起作用。对于集合来说尤其如此,因为迭代始终是一个问题[1]。

另一方面,如果将非线程安全的程序重构为更加线程安全的设计,那么通常不需要线程安全的数据结构。一种流行的方法是根据相互发送“消息”的“参与者”重新定义程序——除了单个生产者/消费者风格的消息队列之外,每个参与者都可以独立运行,不需要使用线程安全数据内部结构。

[1] BCL 集合的第一个版本包括一些“线程安全”集合,这些集合在迭代过程中显然不是线程安全的。并发集合在迭代期间是线程安全的,但会与其他线程的修改同时进行迭代。其他集合库允许“快照”,然后可以对其进行迭代,忽略来自其他线程的修改。

I think what he means is that just using ConcurrentDictionary instead of Dictionary 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, adding SynchronizedAttribute 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.

桃扇骨 2024-11-24 23:55:10

这是一个有点模糊的陈述,但请考虑例如,一个类有两个成员,每个成员都是线程安全的,但都必须以原子方式更新。

在处理这种情况时,您可能会使整个操作成为原子操作,从而成为线程安全的,从而使对各个成员的线程安全访问变得无关紧要。

如果并不意味着您的 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.

舂唻埖巳落 2024-11-24 23:55:10

我的简要解释是这样的。线程安全有多种形式,满足一种形式的代码不会自动满足所有其他形式。

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.

别在捏我脸啦 2024-11-24 23:55:10

罗伊,

我猜你“过度阅读”了一个过于简洁的句子......我将这句话解释为两件事:

  1. 仅仅使用线程安全数据结构并不意味着你的程序可以处理正确的多线程...不仅仅是线程安全数据结构的存在本质上使您的程序成为多线程”;然后他接着说
  2. “除非你准备好投入所涉及的“硬码”(通常需要对相当复杂的场景有非常精确的理解),以使你的整个程序使用线程安全的数据结构正确处理线程基本上是浪费时钟

周期:多线程非常困难,使用适当的开箱即用的数据结构是任何解决方案的重要组成部分,但它肯定不是整个解决方案......除非你'重新准备想清楚(即做你的同步作业)你只是在自欺欺人地说数据结构会以某种方式神奇地“修复”你的程序

我知道这听起来“有点刺耳”,但我的看法是很多。 ,他们真的很失望?!?!

当菜鸟发现编程(在这个动画图标和 GUI 画家的开明时代)需要深入思考时

Roy,

I guess you're "over-reading" a too-concise sentence... I interpret that sentence as meaning two things:

  1. "Just using threadsafe data-structures doesn't mean your program handles multithreading properly... any more than than the presence of threadsafe data-structures inherently makes your program multithreaded"; and he then goes on to say
  2. "Unless you're prepared to put in "the hard yards" involved (it often requires a very precise understanding of quite complex scenarios) to make your WHOLE program handle threading properly, using a threadsafe data-structure is basically a waste of clock-ticks.

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.

久隐师 2024-11-24 23:55:10

该段落告诉我的是
ConcurrentDictionary 没有
保证线程安全写入我的
数据结构?

不,乔·阿尔巴哈里不是这个意思。 ConcurrentDictionary 将始终通过多个线程的同时写入保持一致的状态。另一个线程永远不会看到处于不一致状态的数据结构。

有人可以提供一个
反例表明
线程安全类型实际上失败了
提供线程安全吗?

但是,在多线程环境中,线程安全类型的一系列读取和写入仍然可能失败。

void ExecutedByMultipleThreads(ConcurrentQueue<object> queue)
{
  object value;
  if (!queue.IsEmpty)
  {
    queue.TryDequeue(out value);
    Console.WriteLine(value.GetHashCode());
  }
}

显然,ConcurrentQueue 是线程安全类型,但如果另一个线程将 IsEmpty 之间的最后一个项目出队,则该程序仍可能失败并出现 NullReferenceException和 TryDequeue 方法。数据结构本身仍然通过保持一致状态来提供线程安全保证,但程序不是线程安全的,因为它对线程安全的假设通常是不正确的。在这种情况下,程序是不正确的;不是数据结构。

Is the paragraph telling me that
ConcurrentDictionary does not
guarantee thread-safe writes to my
data structure?

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.

Can someone please provide a
counter-example that shows a
thread-safe type actually failing to
provide thread safety?

However, a series of reads and writes from a thread-safe type may still fail in a multithreaded environment.

void ExecutedByMultipleThreads(ConcurrentQueue<object> queue)
{
  object value;
  if (!queue.IsEmpty)
  {
    queue.TryDequeue(out value);
    Console.WriteLine(value.GetHashCode());
  }
}

So clearly ConcurrentQueue is a thread-safe type, but this program can still fail with a NullReferenceException if another thread dequeued the last item between the IsEmpty and TryDequeue 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文