У меня есть оболочка 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# этого не происходит, как вы можете видеть.