如何使用Parallel.For固定一个通用的Span 实例来处理它?

我正在使用新的Span类型重写我的一些扩展方法,并且我无法找到正确固定通用实例的方法,以便能够使用并行代码来处理它。

例如,考虑这种扩展方法:

 public static unsafe void Fill(this Span span, [NotNull] Func provider) where T : struct { int cores = Environment.ProcessorCount, batch = span.Length / cores, mod = span.Length % cores, sizeT = Unsafe.SizeOf(); //fixed (void* p0 = &span.DangerousGetPinnableReference()) // This doesn't work, can't pin a T object void* p0 = Unsafe.AsPointer(ref span.DangerousGetPinnableReference()); { byte* p = (byte*)p0; // Local copy for the closure Parallel.For(0, cores, i => { byte* start = p + i * batch * sizeT; for (int j = 0; j < batch; j++) Unsafe.Write(start + sizeT * j, provider()); }); // Remaining values if (mod == 0) return; for (int i = span.Length - mod; i < span.Length; i++) span[i] = provider(); } } 

在这里,我只想使用一些值提供程序填充输入Span ,因为这些向量可能非常大,所以我想并行填充它们。

这只是一个例子,所以即使在这里使用并行代码不是100%必要的,问题仍然存在,因为我需要迟早再次使用并行代码。

现在,这段代码确实有效 ,但是因为我从来没有实际固定输入范围并且事实上它很可能指向一些托管的T[]向量,它可以被GC一直移动,我我想我可能很幸运,看到它在我的测试中运行良好。

所以,我的问题是:

有没有办法固定一个通用的Span实例并获得一个简单的void*指针,以便我可以在闭包中传递它来处理并行代码中的Span实例?

谢谢!

我想我可能已经找到了使用Unsafe类中的一个新方法的解决方法,我已经对它进行了测试,到目前为止似乎有效。 这里是:

 public static unsafe void Fill(this Span span, [NotNull] Func provider) where T : struct { int cores = Environment.ProcessorCount, batch = span.Length / cores, mod = span.Length % cores, size = Unsafe.SizeOf(); ref T r0 = ref span.DangerousGetPinnableReference(); fixed (byte* p0 = &Unsafe.As(ref r0)) { byte* p = p0; Parallel.For(0, cores, i => { byte* pi = p + i * batch * size; for (int j = 0; j < batch; j++, pi += size) Unsafe.Write(pi, provider()); }).AssertCompleted(); // Remaining values if (mod < 1) return; for (int i = span.Length - mod; i < span.Length; i++) Unsafe.Write(p + i * size, provider()); } } 

基本上,因为我无法固定ref T值,所以我尝试使用Unsafe.As(ref T value)来获取ref byte变量Unsafe.As(ref T value)并将其固定。 因为它指向相同的地址,我认为(希望)它固定得很好,它应该在IL中做同样的事情。