如何并行对 Span<T> 进行操作?

Vla*_*adu 6 c# parallel-processing system.memory

我想并行执行操作Span<T>,但这样的操作是不合法的:

void DoSomething(Span<int> buffer, int option1, int option2)
{
   ....... 
}

void ParallelDoSomething(Span<int> buffer)
{
    var size = buffer.Length;
    Parallel.Invoke(() => DoSomething(buffer, 0, size / 2),
        () => DoSomething(buffer, size/2, size)); //not legal
}
Run Code Online (Sandbox Code Playgroud)

因为编译器抱怨:不能在匿名方法、lambda 表达式、查询表达式或本地函数中使用 ref、out 或 in 参数“缓冲区”

如何在以Span<T>参数为参数的并行方法中执行?

Mar*_*ell 8

这里的问题是 aSpan<T>不能被允许进入堆 - 它只堆栈上有效 - 这意味着它不能被装箱class,也不能用作 a (或struct除 a 之外的 a )上的字段ref struct。这排除了捕获的变量和最常见的state参数形式。

如果您可以将输入更改为内存,则可以捕获内存并获取lambda 体内跨度:

void ParallelDoSomething(Memory<int> memory)
{
    var size = memory.Length;
    Parallel.Invoke(
        () => DoSomething(memory.Span, 0, size / 2),
        () => DoSomething(memory.Span, size/2, size)
    );
}
Run Code Online (Sandbox Code Playgroud)

如果你不能改变输入,你仍然可以通过作弊来做到这一点。您可以固定现有跨度,创建覆盖该固定数据的内存,然后使用内存,就像它作为内存传入一样。这并不完全是微不足道的,因为您需要编写自己的基于指针的内存管理器实现,但是:它可以工作。这是来自 protobuf-net 的示例:https://github.com/protobuf-net/protobuf-net/blob/main/src/protobuf-net.Core/Meta/TypeModel.cs#L767-L789

或者也许更方便,固定跨度并直接捕获指针,注意编译器通常不允许这样做(以防止稍后由委托使用指针),但由于我们知道计时语义,所以我们可以让它高兴通过复制指针:

unsafe void ParallelDoSomething(Span<int> span)
{
    var size = span.Length;
    fixed (int* ptr = span)
    {
        int* evil = ptr; // make the compiler happy
        Parallel.Invoke(
            () => DoSomething(new Span<int>(evil, size), 0, size / 2),
            () => DoSomething(new Span<int>(evil, size), size / 2, size)
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

或者如果我们想在输入点修复跨度切片:

unsafe void ParallelDoSomething(Span<int> span)
{
    var size = span.Length;
    fixed (int* ptr = span)
    {
        int* evil = ptr; // make the compiler happy
        Parallel.Invoke(
            () => DoSomething(new Span<int>(evil, size / 2));
            () => DoSomething(new Span<int>(evil + (size/2), size - (size/2));
        );
    }
}
Run Code Online (Sandbox Code Playgroud)