为什么从 C# 到 C++ P/Invoking 时会收到 _CrtIsValidHeapPointer(block)?

问题描述 投票:0回答:1

我有一个简单的方法,应该使用本机代码模糊图像:

internal override void Apply(BitmapBuffer framebuffer, 
    BitmapBuffer backBuffer, 
    BitmapBuffer frontBuffer, 
    BitmapBufferRepository repository)
{
    var data = framebuffer.Bitmap.LockBits(
        new System.Drawing.Rectangle(0, 0, framebuffer.Bitmap.Width, framebuffer.Bitmap.Height), 
        System.Drawing.Imaging.ImageLockMode.ReadWrite, 
        System.Drawing.Imaging.PixelFormat.Format32bppArgb);

    ImageProcessing.GaussianBlur(data.Scan0, 
        data.Stride, 
        data.Width, 
        data.Height, 
        Radius);

    framebuffer.Bitmap.UnlockBits(data);
}

GaussianBlur方法定义如下:

[DllImport("Animator.Engine.Native.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GaussianBlur(IntPtr bitmapData,
    int stride,
    int width,
    int height,
    int radius);

在 C++ 中:

extern "C" void __cdecl GaussianBlur(unsigned char* bitmapData,
    int stride,
    int width,
    int height,
    int radius)
{
    // Gaussian kernel

    int diameter = 2 * radius + 1;
    std::shared_ptr<float[]> kernel = generateGaussKernel(diameter);

    // Blur

    auto copy = std::shared_ptr<unsigned char[]>(new unsigned char[height * stride]);

    for (int y = 0; y < height; y++)
        memcpy(copy.get() + y * stride, bitmapData + y * stride, width * BYTES_PER_PIXEL);

    for (int y = 0; y < height; y++)
        for (int x = 0; x < width; x++)
        {
            FloatColor sum;
            float weight = 0.0f;
            int count = 0;

            int xStart = x - radius;
            int xEnd = x + radius;
            int yStart = y - radius;
            int yEnd = y + radius;

            for (int x1 = xStart; x1 <= xEnd; x1++)
                for (int y1 = yStart; y1 <= yEnd; y1++)
                {
                    // Find weight

                    int kernelX = x1 - xStart;
                    int kernelY = y1 - yStart;
                    float kernelValue = kernel[kernelY * diameter + kernelX];

                    // Premultiply alpha

                    FloatColor color;
                    if (x1 >= 0 && x1 < width && y1 >= 0 && y1 < height)
                        color = getFloatColor(copy.get(), stride, x1, y1);
                    else
                        color = FloatColor(0);

                    sum.R += (float)(color.R * color.A) * kernelValue;
                    sum.G += (float)(color.G * color.A) * kernelValue;
                    sum.B += (float)(color.B * color.A) * kernelValue;
                    sum.A += color.A * kernelValue;

                    weight += kernelValue;
                    count++;
                }

            if (count > 0)
            {
                FloatColor result;
                result.A = sum.A / weight;

                if (result.A > 0)
                {
                    result.R = ((sum.R / weight) / result.A);
                    result.G = ((sum.G / weight) / result.A);
                    result.B = ((sum.B / weight) / result.A);
                }

                setFloatColor(bitmapData, stride, x, y, result);
            }
        }
}

当我的应用程序在多线程环境中运行时(

Parallel.For
),尝试调用
GaussianBlur
会导致“调试断言失败”错误:

---------------------------
Microsoft Visual C++ Runtime Library
---------------------------
Debug Assertion Failed!

Program: ...nimator\Animator\bin\x64\Debug\net6.0-windows7.0\Animator.exe
File: minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp
Line: 904

Expression: _CrtIsValidHeapPointer(block)

For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.

(Press Retry to debug the application)

---------------------------
Przerwij   Ponów próbę   Ignoruj   
---------------------------

事实证明,在 P/Invoke 实际完成之前断言就失败了(本机函数中第一行的断点从未命中)。

我做错了什么?


编辑:我刚刚检查过,当第二个线程尝试调用该函数时,断言失败(有时)会发生。

顺便说一句,我的 C++ 代码中的所有函数都是线程安全实现的,特别是除了通过参数传递的资源之外,它们都没有使用任何外部资源。

c# c++ pointers parallel-processing
1个回答
0
投票

事实证明,我对线程安全实现的函数撒了谎。导致问题的两个函数都使用了函数“generateGaussKernel”,该函数不是线程安全的。我用以下方法修复了它:

std::shared_ptr<float[]> generateGaussKernel(int diameter)
{
    // *** HERE ***
    EnterCriticalSection(&gaussKernelCriticalSection);

    while (gaussKernels.size() <= diameter)
        gaussKernels.push_back(std::shared_ptr<float[]>(nullptr));

    if (gaussKernels[diameter].get() == nullptr)
    {
        float sigma = diameter / 4.0f;
        std::shared_ptr<float[]> kernel(new float[diameter * diameter]);
        int mean = diameter / 2;
        float sum = 0.0; // For accumulating the kernel values
        for (int x = 0; x < diameter; ++x)
            for (int y = 0; y < diameter; ++y) {
                kernel[y * diameter + x] = (float)(exp(-0.5 * (pow((x - mean) / sigma, 2.0) + pow((y - mean) / sigma, 2.0))) / (2 * M_PI * sigma * sigma));

                // Accumulate the kernel values
                sum += kernel[y * diameter + x];
            }

        // Normalize the kernel
        for (int x = 0; x < diameter; ++x)
            for (int y = 0; y < diameter; ++y)
                kernel[y * diameter + x] /= sum;

        gaussKernels[diameter] = std::shared_ptr<float[]>(kernel);
    }

    // *** AND HERE ***
    LeaveCriticalSection(&gaussKernelCriticalSection);

    return gaussKernels[diameter];
}

显然还需要额外调用

InitializeCriticalSection()
DeleteCriticalSection()

现在错误已经消失了。

© www.soinside.com 2019 - 2024. All rights reserved.