shm*_*lie 6 c# c++ pinvoke task-parallel-library std-future
我正在包装要从.NET使用的C ++库。C ++ API中有返回的函数std::future。我想让.NET代理类返回System.Threading.Tasks.Task。
我想到了在C ++端添加一个替换方法,该方法除了方法参数外还将包含一个函数指针。然后,我可以启动一个新线程(std::async例如,使用)并等待std::future::get。一旦std::future::get返回,我可以调用传入的函数指针。在C#端,我会将指针传递给一个函数,该函数将完成返回的Task。就像是:
Task CallCpp(int a, int b) {
TaskCompletionSource<int> tcs;
Action callback = () => tcs.SetResult(0);
IntPtr callbackPtr = Marshal.GetFunctionPointerForDelegate(callback);
CppMethod(callbackPtr, a, b);
return tcs.Task;
}
[DllExport]
external void CppMethod(IntPtr callback, int a, int b);
Run Code Online (Sandbox Code Playgroud)
void CppMethod(void (*callback)(), int a, int b) {
std::future f = realmethod(a, b);
std::async([&]{ f.get; callback(); });
}
std::future realmethod(int a, int b);
Run Code Online (Sandbox Code Playgroud)
(是的,代码中有内存管理问题。尽管这样足以使您理解这个想法)
编组异步完成或失败是相对容易的部分。我个人会使用用 C# 实现的 COM 接口,标记为,并带有和[InterfaceType( ComInterfaceType.InterfaceIsIUnknown )]等 2 个方法,并在启动任务时传递它,但这只是个人喜好的问题。setCompleted()setFailed( HRESULT status )
问题是缺乏C++多线程实现。
\n\nBoost 在这方面更好,它\xe2\x80\x99sfuture类具有then 可用于传播状态而不阻塞线程的方法。
目前,标准 C++ 期货不\xe2\x80\x99 提供任何类似的东西。我想你运气不好。
\n\n如果您可以修改该 C++ 库,将 API 更改为更有意义的内容,即可以以非阻塞方式通知完成情况。
\n\n如果没有 COM,生命周期管理就会变得复杂。在您\xe2\x80\x99 启动异步任务之后但在其完成之前,C# 端的某个人需要保留该函数指针。Here\xe2\x80\x99s 是一个可能的解决方案,注意它如何使用 GCHandle 使回调比任务更持久:
\n\n[UnmanagedFunctionPointer( CallingConvention.Cdecl )]\ndelegate void pfnTaskCallback( int hResult );\n\n[DllImport( "my.dll" )]\nstatic extern void launch( [MarshalAs( UnmanagedType.FunctionPtr )] pfnTaskCallback completed );\n\npublic static async Task runAsync()\n{\n var tcs = new TaskCompletionSource<bool>();\n pfnTaskCallback pfn = delegate ( int hr )\n {\n if( hr >= 0 ) // SUCEEDED\n tcs.SetResult( true );\n else\n tcs.SetException( Marshal.GetExceptionForHR( hr ) );\n };\n var gch = GCHandle.Alloc( pfn, GCHandleType.Normal );\n\n try\n {\n launch( pfn );\n await tcs.Task;\n }\n finally\n {\n gch.Free();\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n