gpt4 book ai didi

c# - 为什么 c# 迭代器跟踪创建线程而不是使用互锁操作?

转载 作者:行者123 更新时间:2023-11-30 12:33:50 27 4
gpt4 key购买 nike

自从我在 Jon Skeet 的 site 上读到迭代器以来,这一直困扰着我。 .

Microsoft 使用其自动迭代器实现了一个简单的性能优化 - 返回的 IEnumerable 可以作为 IEnumerator 重复使用,从而节省了对象创建。现在因为 IEnumerator 必然需要跟踪状态,所以这仅在第一次迭代时有效。

我无法理解的是,为什么设计团队采用他们所做的方法来确保线程安全。

通常,当我处于类似的位置时,我会使用我认为是简单的 Interlocked.CompareExchange - 以确保只有一个线程设法将状态从“可用”更改为“处理中”。

从概念上讲它非常简单,一个原子操作,不需要额外的字段等。

但是设计团队的方法呢?每个 IEnumerable 都保留创建线程的托管线程 ID 的字段,然后在针对该字段调用 GetEnumerator 时检查该线程 ID,只有当它是同一个线程并且是第一次调用时,IEnumerable 才能返回自身作为 IEnumerator。在我看来,这似乎更难推理。

我只是想知道为什么要采用这种方法。 Interlocked 操作是否比两次调用 System.Threading.Thread.CurrentThread.ManagedThreadId 慢得多,以至于它证明了额外字段的合理性?

或者这背后是否有其他原因,可能涉及内存模型或 ARM 设备或我没有看到的东西?也许规范对 IEnumerable 的实现提出了具体要求?只是真的很困惑。

最佳答案

我不能肯定地回答,但是关于你的问题:

Are Interlocked operations far slower than two calls to System.Threading.Thread.CurrentThread.ManagedThreadId, so much so that it justifies the extra field?

是的,互锁操作比两次获取 ManagedThreadId 的调用要慢得多 - 互锁操作并不便宜,因为它们需要多 CPU 系统来同步它们的缓存。

来自 Understanding the Impact of Low-Lock Techniques in Multithreaded Apps :

Interlocked instructions need to ensure that caches are synchronized so that reads and writes don't seem to move past the instruction. Depending on the details of the memory system and how much memory was recently modified on various processors, this can be pretty expensive (hundreds of instruction cycles).

Threading in C# ,它将开销列为 10ns。而获取 ManagedThreadId 应该是对静态数据的正常非锁定读取。

现在这只是我的猜测,但如果您考虑正常用例,那就是调用函数来检索 IEnumerable 并立即迭代一次。所以在标准用例中,对象是:

  1. 使用过一次
  2. 在创建它的同一个线程上使用
  3. 短暂的

所以这种设计没有带来同步开销,并牺牲了 4 个字节,这可能只会在很短的时间内使用。

当然,要证明这一点,您必须进行性能分析以确定相对成本,并进行代码分析以证明常见情况。

关于c# - 为什么 c# 迭代器跟踪创建线程而不是使用互锁操作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8291634/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com