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>参数为参数的并行方法中执行?
这里的问题是 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)
| 归档时间: |
|
| 查看次数: |
1399 次 |
| 最近记录: |