如何在C#中基于Span类类型实现并行/异步代码?
解决方案:Span2D的并行/异步处理
1. 优先使用同步并行(无额外开销)
Span2D<T>作为ref struct无法被异步闭包捕获,但可以通过同步并行API(如Parallel.Invoke)实现多线程处理。这类API不会将Span2D传入异步上下文,而是在线程池线程上同步执行,且Span2D的生命周期由当前方法保证,完全符合安全要求。
修改后的代码如下:
namespace Example; public static class ExampleMethod { public static void TransformExample<T>(Span2D<T> data) { var halfRows = data.Height / 2; var halfCols = data.Width / 2; // 使用Parallel.Invoke实现同步并行,规避Span传递限制 Parallel.Invoke( () => TransformQuadrant(data[..halfRows, ..halfCols]), () => TransformQuadrant(data[..halfRows, halfCols..]), () => TransformQuadrant(data[halfRows.., ..halfCols]), () => TransformQuadrant(data[halfRows.., halfCols..]) ); } private static void TransformQuadrant<T>(Span2D<T> data) { // 对象限数据执行处理逻辑 } }
Parallel.Invoke会阻塞当前线程直到所有任务完成,效果和Task.WaitAll一致,但完全避免了ref struct的捕获问题,且无任何额外开销。
2. 异步场景用Memory2D(开销可忽略)
如果必须释放当前线程(真正的异步场景),则需要使用Memory2D<T>替代Span2D<T>作为方法参数。Memory2D<T>不是ref struct,可以安全传递给Lambda表达式;而从Memory2D<T>转换为Span2D<T>只是获取内存视图,没有内存拷贝,开销极小,几乎不会影响性能。
示例代码:
namespace Example; public static class ExampleMethod { public static async Task TransformExampleAsync<T>(Memory2D<T> data) { var halfRows = data.Height / 2; var halfCols = data.Width / 2; var tasks = new List<Task> { Task.Run(() => TransformQuadrant(data[..halfRows, ..halfCols].Span)), Task.Run(() => TransformQuadrant(data[..halfRows, halfCols..].Span)), Task.Run(() => TransformQuadrant(data[halfRows.., ..halfCols].Span)), Task.Run(() => TransformQuadrant(data[halfRows.., halfCols..].Span)) }; await Task.WhenAll(tasks); } private static void TransformQuadrant<T>(Span2D<T> data) { // 处理逻辑保持不变 } }
3. 极端性能场景的不安全指针方案(不推荐)
如果确实需要完全避免Memory2D<T>的转换(实际中几乎没必要),可以使用不安全代码直接操作内存指针,但这会增加代码复杂度和出错风险,仅适合极端性能敏感场景:
namespace Example; public static unsafe class ExampleMethod { public static void TransformExample<T>(Span2D<T> data) where T : unmanaged { var halfRows = data.Height / 2; var halfCols = data.Width / 2; var elementSize = sizeof(T); var rowStride = data.Stride; Parallel.Invoke( () => { fixed (T* ptr = data) { var quadrant = new Span2D<T>(ptr, halfRows, halfCols, rowStride); TransformQuadrant(quadrant); } }, () => { fixed (T* ptr = data) { var quadrant = new Span2D<T>(ptr + halfCols, halfRows, halfCols, rowStride); TransformQuadrant(quadrant); } }, () => { fixed (T* ptr = data) { var quadrant = new Span2D<T>(ptr + halfRows * rowStride / elementSize, halfRows, halfCols, rowStride); TransformQuadrant(quadrant); } }, () => { fixed (T* ptr = data) { var quadrant = new Span2D<T>(ptr + halfRows * rowStride / elementSize + halfCols, halfRows, halfCols, rowStride); TransformQuadrant(quadrant); } } ); } private static void TransformQuadrant<T>(Span2D<T> data) { // 处理逻辑 } }
内容的提问来源于stack exchange,提问作者Rui Oliveira
相关产品推荐
相关产品推荐

