首先要做的事情:
Span<T>
和Memory<T>
是什么Span<T>
必须只驻留在Stack上我还不清楚:对于Memory<T>
来说,结构撕裂是不是一个问题?据我所知,基本上每个大于WORD大小的类型都会受到影响。更进一步,当这种类型可用于多线程读写器场景时,它可能导致竞争条件,如下面的链接所述。
为了达到这一点:当使用this example代替Memory<T>
时,Span<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
和Memory<T>.Span
。
需要注意的重要一点是(据我可以解决)我们不关心以某种方式撕裂,这意味着我们仍然指向我们引用的对象中的某个位置 - 尽管我们的调用者可能会得到一些奇怪的数据它没有预料到,这是安全的,因为他们不会得到AccessViolationException。由于对字段进行了不同步的线程访问,它们只会产生竞争条件,从而产生意外结果。
Span<T>
从Memory<T>
获得了this comment。它有Memory<T>
:
如果Memory或ReadOnlyMemory实例被撕裂,则此属性getter具有未定义的行为。我们尝试检测这种情况并抛出一个异常,但是撕裂的结构可能在我们看来是有效的,并且我们将返回一个不需要的跨度。与原始内存实例相比,这样的跨度始终保证至少是入站,因此使用跨度将不会AV过程。
所以,我们绝对可以撕裂Span<T>
,然后尝试从中创建一个Memory<T>
。在这种情况下,如果Memory<T>
以这样的方式撕裂,它现在引用了Span<T>
引用的对象之外的某些内存,那么代码中会检查抛出异常。
如果它仍然以某种方式撕裂它仍然引用原始对象中的某个地方,那就没关系 - 我们的调用者可能没有读取它期望读取的东西,但至少他们不会得到AccessViolationException,这是什么我们试图避免。
请注意,Memory<T>
无法实现相同的检查(即使它想要)。 Span<T>
保持对对象,起始偏移和长度的引用。 Memory<T>.Pin()
只保留对象内部的一些内存地址和长度的引用。
unsafe
是this comment方法,有unsafe
:
如果结构被撕裂,下面的逻辑可能会导致AV。这是好的,因为调用者期望使用原始指针,我们不需要将其保持为与其他基于Span的API一样安全。
同样,我们可以撕裂我们不再引用我们所指的对象内部的某个地方。然而,这种方法是qazxswpoi,我们不在乎。