6

我正在包装一个要从 .NET 使用的 C++ 库。C++ API 中有一些函数返回std::future. 我想让 .NET 代理类返回System.Threading.Tasks.Task

我想在 C++ 端添加一个替换方法,除了方法参数之外,它还需要一个函数指针。然后我可以启动一个新线程(std::async例如使用)并等待std::future::get。一旦std::future::get返回,我可以调用传入的函数指针。在 C# 方面,我会将 a 指针传递给将完成返回的任务的函数。就像是:

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);
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);

(是的,代码存在内存管理问题。不过应该足以理解这个想法)

4

1 回答 1

2

编组异步完成或失败是相对容易的部分。我个人会使用用 C# 实现的 COM 接口,标记为[InterfaceType( ComInterfaceType.InterfaceIsIUnknown )],带有 2 种方法,例如setCompleted()and setFailed( HRESULT status ),并在我开始任务时传递它,但这是个人喜好问题。

问题是缺少 C++ 多线程实现。

Boost 在这方面更好,它的future类具有then 可用于在不阻塞线程的情况下传播状态的方法。

目前,标准 C++ 期货不提供任何可比的东西。我觉得你运气不好。

如果您可以修改该 C++ 库,请将 API 更改为更有意义的内容,即可以以非阻塞方式通知完成。


如果没有 COM,生命周期管理会很复杂。在您启动异步任务之后但在它完成之前,C# 端的某个人需要保留该函数指针。这是一个可能的解决方案,请注意它如何使用 GCHandle 使回调比任务更有效:

[UnmanagedFunctionPointer( CallingConvention.Cdecl )]
delegate void pfnTaskCallback( int hResult );

[DllImport( "my.dll" )]
static extern void launch( [MarshalAs( UnmanagedType.FunctionPtr )] pfnTaskCallback completed );

public static async Task runAsync()
{
    var tcs = new TaskCompletionSource<bool>();
    pfnTaskCallback pfn = delegate ( int hr )
    {
        if( hr >= 0 )   // SUCEEDED
            tcs.SetResult( true );
        else
            tcs.SetException( Marshal.GetExceptionForHR( hr ) );
    };
    var gch = GCHandle.Alloc( pfn, GCHandleType.Normal );

    try
    {
        launch( pfn );
        await tcs.Task;
    }
    finally
    {
        gch.Free();
    }
}
于 2019-05-15T16:58:00.973 回答