struct tearing 是 Memory<T> 的问题吗?
Is struct tearing an issue for Memory<T>?
要事第一:
我知道 Span<T>
和 Memory<T>
是什么
我知道为什么 Span<T>
必须只驻留在堆栈上
我(概念上)知道结构撕裂是什么
我还不清楚的是:
结构撕裂不是 Memory<T>
的问题吗?据我了解,基本上所有大于 WORD 大小 can/will 的类型都会受到影响。更进一步,当这种类型可以在多线程 reader-writer-scenario 中使用时,它可能会导致竞争条件,如下面 link 中所述。
直截了当:当使用 Span<T>
代替 Span<T>
:
时,this example 不会引发潜在不一致 Memory<T>
对象的问题
internal class Buffer {
Memory<byte> _memory = new byte[1024];
public void Resize(int newSize) {
_memory = new byte[newSize]; // Will this update atomically?
}
public byte this[int index] => _memory.Span[index]; // Won't this also possibly see partial update?
}
根据implementation of CoreFX Memory<T>
还依次布置了一个(托管对象)引用、它的长度和一个索引。我遗漏的 Span<T>
有什么区别,这使得 Memory<T>
适合那些场景?
看了Memory<T>
里的评论,看来绝对可以撕
但是,似乎有两个地方确实很重要:Memory<T>.Pin()
和 Memory<T>.Span
。
需要注意的重要一点是(据我所知)我们不关心撕裂,这意味着我们仍然指向某处我们引用的对象——虽然我们的调用者可能会得到一些它没有预料到的奇怪数据,但从他们不会得到 AccessViolationException 的意义上来说这是安全的。由于对字段的线程访问不同步,它们只会产生一个会产生意外结果的竞争条件。
Memory<T>.Span
从 Memory<T>
得到一个 Span<T>
。它有 this comment:
If the Memory or ReadOnlyMemory instance is torn, this property getter has undefined behavior. We try to detect this condition and throw an exception, but it's possible that a torn struct might appear to us to be valid, and we'll return an undesired span. Such a span is always guaranteed at least to be in-bounds when compared with the original Memory instance, so using the span won't AV the process.
所以,我们绝对可以撕开 Memory<T>
,然后尝试从中创建一个 Span<T>
。在这种情况下,如果 Memory<T>
以现在引用 Memory<T>
.[= 引用的对象之外的某些内存的方式撕裂,则会抛出异常的代码检查。 30=]
如果它以某种方式撕裂,但它仍然引用原始对象中的某处,那没关系 - 我们的调用者可能没有阅读它期望阅读的内容,但至少他们不会得到AccessViolationException,这是我们要避免的。
请注意,Span<T>
无法执行相同的检查(即使它想执行)。 Memory<T>
保留对对象、起始偏移量和长度的引用。 Span<T>
仅保留对对象内部某些内存地址和长度的引用。
Memory<T>.Pin()
是一个 unsafe
方法,它有 this comment:
It's possible that the below logic could result in an AV if the struct is torn. This is ok since the caller is expecting to use raw pointers, we're not required to keep this as safe as the other Span-based APIs.
同样,我们可以以不再引用我们引用的对象内部某处的方式进行撕裂。不过这个方法是unsafe
,我们不在乎。
要事第一:
我知道
Span<T>
和Memory<T>
是什么我知道为什么
Span<T>
必须只驻留在堆栈上我(概念上)知道结构撕裂是什么
我还不清楚的是:
结构撕裂不是 Memory<T>
的问题吗?据我了解,基本上所有大于 WORD 大小 can/will 的类型都会受到影响。更进一步,当这种类型可以在多线程 reader-writer-scenario 中使用时,它可能会导致竞争条件,如下面 link 中所述。
直截了当:当使用 Span<T>
代替 Span<T>
:
Memory<T>
对象的问题
internal class Buffer {
Memory<byte> _memory = new byte[1024];
public void Resize(int newSize) {
_memory = new byte[newSize]; // Will this update atomically?
}
public byte this[int index] => _memory.Span[index]; // Won't this also possibly see partial update?
}
根据implementation of CoreFX Memory<T>
还依次布置了一个(托管对象)引用、它的长度和一个索引。我遗漏的 Span<T>
有什么区别,这使得 Memory<T>
适合那些场景?
看了Memory<T>
里的评论,看来绝对可以撕
但是,似乎有两个地方确实很重要:Memory<T>.Pin()
和 Memory<T>.Span
。
需要注意的重要一点是(据我所知)我们不关心撕裂,这意味着我们仍然指向某处我们引用的对象——虽然我们的调用者可能会得到一些它没有预料到的奇怪数据,但从他们不会得到 AccessViolationException 的意义上来说这是安全的。由于对字段的线程访问不同步,它们只会产生一个会产生意外结果的竞争条件。
Memory<T>.Span
从 Memory<T>
得到一个 Span<T>
。它有 this comment:
If the Memory or ReadOnlyMemory instance is torn, this property getter has undefined behavior. We try to detect this condition and throw an exception, but it's possible that a torn struct might appear to us to be valid, and we'll return an undesired span. Such a span is always guaranteed at least to be in-bounds when compared with the original Memory instance, so using the span won't AV the process.
所以,我们绝对可以撕开 Memory<T>
,然后尝试从中创建一个 Span<T>
。在这种情况下,如果 Memory<T>
以现在引用 Memory<T>
.[= 引用的对象之外的某些内存的方式撕裂,则会抛出异常的代码检查。 30=]
如果它以某种方式撕裂,但它仍然引用原始对象中的某处,那没关系 - 我们的调用者可能没有阅读它期望阅读的内容,但至少他们不会得到AccessViolationException,这是我们要避免的。
请注意,Span<T>
无法执行相同的检查(即使它想执行)。 Memory<T>
保留对对象、起始偏移量和长度的引用。 Span<T>
仅保留对对象内部某些内存地址和长度的引用。
Memory<T>.Pin()
是一个 unsafe
方法,它有 this comment:
It's possible that the below logic could result in an AV if the struct is torn. This is ok since the caller is expecting to use raw pointers, we're not required to keep this as safe as the other Span-based APIs.
同样,我们可以以不再引用我们引用的对象内部某处的方式进行撕裂。不过这个方法是unsafe
,我们不在乎。