Распределенная память не освобождается, когда C# приложение заканчивается - PullRequest
3 голосов
/ 17 апреля 2020

У меня есть оболочка C ++ вокруг Python модуля, который прекрасно работает в C ++. То есть утечки памяти нет вообще. Я раскрыл функциональность C ++ в C и использовал его в C# приложении. Однако я заметил, что всякий раз, когда я заканчиваю приложение C#, выделенная память не освобождается!

Ирония в том, что все делается в Python, а в C ++ нет выделения памяти, это просто серия методов, которые вызывают свои Python аналоги, а в Python вся память управляется интерпретатором. Модуль Python в основном работает с бесконечным l oop, в котором используется канал веб-камеры, а изображения обрабатываются и отправляются обратно клиентам (C#, C ++). В Python есть метод Stop, который просто устанавливает значение, которое завершает этот l oop в python, поэтому, когда этот l oop заканчивается, фактически все заканчивается и, таким образом, освобождается.

Примечание:
Память, которую вы видите, связана с моделью, которая загружается в память, и эта модель управляется Pytorch (инфраструктура глубокого изучения в Python) так что в части Python у нас просто есть вызовы других управляемых кодов.

C ++ просто вызывает эти методы, и у меня нет проблем в Python или C ++, но когда дело доходит до C#, выделенная память не освобождается!

В случае, если это имеет значение, вот мои DLLImport операторы по C#:

[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Initialize(bool showFeed);

[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Start(bool async);

[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Stop();

[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetCpuAffinity(int mask);

[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool GetVideoFeedDbgStatus();

[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetVideoFeedDbgStatus(bool status);

public delegate void CallbackDelegate(bool status, string message, IntPtr img, int rows, int cols);
[MethodImplAttribute(MethodImplOptions.InternalCall)]

[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void AddCallback(IntPtr fn);

[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void RemoveCallback(IntPtr fn);

[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern  IntPtr GetCallbacks_str();

И это обратный вызов C#:

int op=0;
public void callback01(bool status, string id, IntPtr img_ptr, int rows, int cols)
{
     this.status = status;
     this.id = id;
     int[] sizes = { rows, cols };

     img = new Mat(sizes, MatType.CV_8UC3, img_ptr);
     // this is to retain the image, as the callback is so fast
     // the time c# will draw it, it will be invalidated and thus you'd get
     // distorted image. sowe make a deep copy here so we can work with images
     // at our own pace!
     img2 = new Mat();
     img.CopyTo(img2);

     //this allows to execute all interop calls under one thread
     switch (op)
     {
          case 1:
              Stop();
              //MessageBox.Show("done.");
              t.Abort();
              break;
          case 2:
              MessageBox.Show(GetVideoFeedDbgStatus().ToString());
              break;
          default:
              break;
      }
      //resetting the flag
      op = 0;
}

Как это запускается:

Thread t;
private void btnRunService_Click(object sender, EventArgs e)
{
    tmrFetchStatus.Start();
    t = new Thread(new ThreadStart(() =>
    {
        RunService();
    }));
    t.IsBackground = true;
    t.Start();
}

void RunService()
{
    btnInit_Click(null, null);
    Start(chkboxAsync.Checked);
}

private void btnInit_Click(object sender, EventArgs e)
{
    Initialize(chkbxIShowFeed.Checked);
    SetUpCallback();
    tmrFetchStatus.Enabled = true;
}

и вот как это заканчивается:

private void btnStop_Click(object sender, EventArgs e)
{
     op = 1;
}

и C открытые методы выглядят так:

extern "C"
{
    Core* core = nullptr;

    CORE_API int Initialize(bool showFeed)
    {
        return CreateHandle(showFeed) != nullptr ? 0 : 1;
    }

    CORE_API void Start(bool async)
    {
        core = reinterpret_cast<Core*>(GetHandle());
        core->Start(async);
    }

    CORE_API void Stop(void)
    {
        core = reinterpret_cast<Core*>(GetHandle());
        core->Stop();
    }

    CORE_API void AddCallback(CCallbackFn callback)
    {
        core->AddCallback_C_tmp(callback);
    }

    CORE_API void RemoveCallback(CCallbackFn callback)
    {
        core->RemoveCallback_C_tmp(callback);
    }

    std::string callbackLst;
    CORE_API const char* GetCallbacks_str(void)
    {
        core = reinterpret_cast<Core*>(GetHandle());
        auto results = core->GetCallbacks_C_tmp();

        // this is shared, so clear it each time. 
        callbackLst = "";
        for (auto pair : results)
        {
            callbackLst += pair.first + "\r\n";
        }

        //size_t size = callbackLst.size() * sizeof(char) + 1;
        //char* output = new char[size];
        //strcpy_s(output, size, callbackLst.c_str());

        return callbackLst.c_str();
    }

    CORE_API CCallbackFn* GetCallbacks()
    {
        core = reinterpret_cast<Core*>(GetHandle());
        auto results = core->GetCallbacks_C_tmp();
        size_t size = results.size() * sizeof(CCallbackFn) + 1;
        CCallbackFn* callback_list = new CCallbackFn[size];
        int i = 0;
        for (auto pair : results)
        {
            callback_list[i] = pair.second;
        }
        return callback_list;
    }

}

А в части C ++, которая использует / вызывает модуль Python:


    this->serviceUtilsModule = py::module::import("SomeModule");
    this->cls = this->serviceUtilsModule.attr("SomeClass");
    //...
    this->startFunc = this->obj.attr("start");
    this->startFuncAsync = this->obj.attr("start_async");
    this->stopFunc = this->obj.attr("stop");
...
}
//...
CORE_API void Core::Start(bool async)
{
    try
    {
        if (async)
        {
            this->startFuncAsync();
        }
        else
        {
            this->startFunc();
        }
    }
    catch (std::exception ex)
    {
        std::cout << ex.what() << std::endl;
    }
}

CORE_API void Core::Stop(void)
{
    this->stopFunc();
}

Что может быть причиной здесь? Что я должен делать, что я не? А вот небольшая демонстрация, демонстрирующая разницу между приложением C ++ и C# (C ++ не имеет проблем с памятью, в то время как в C# мне приходится вручную освобождать память!): https://imgur.com/a/eaW9AYT

А вот второй клип, показывающий приложение C# внутри vs (дополнительная информация об отладке): https://imgur.com/a/wL6UUOV

Приложение C ++ просто start с, а затем через 10 секунд вызовы прекращаются, и когда это происходит, вся память освобождается. Однако в C# этого не происходит, как вы можете видеть.

1 Ответ

0 голосов
/ 18 апреля 2020

Я нашел проблему. Кажется, проблема была вызвана завершением потока внутри самого себя, который находится внутри callback01, который сам был запущен, скажем, поток 2, мы пытались завершить этот самый поток:

...
     switch (op)
     {
          case 1:
              Stop();
              //MessageBox.Show("done.");
              t.Abort();
              break;
          case 2:
              MessageBox.Show(GetVideoFeedDbgStatus().ToString());
              break;
          default:
              break;
      }

Stop делает остановите python l oop, кажется, но это не освобождает память как можно скорее! как я полагаю, потому что модуль python не выгружен, объект python все еще существует и, таким образом, сохраняет свою память. Однако закрытие приложения C# освобождает память.
Поскольку поток помечен как фон, закрытие приложения также должно завершить его.

Примечание:
Интересно, почему я не получил никаких исключений по этому поводу в C#!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...