You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 12:20:03