使用Task <t> </t>包装本机异步函数

时间:2013-10-21 13:42:42

标签: c# task-parallel-library pinvoke native async-await

我正在尝试围绕用本机代码编写的第三方库编写一个c#包装器,以便在我们的应用程序中使用,这些应用程序几乎都是用.NET编写的,我正在努力保持对C#模式的忠诚。这个库中的几乎所有调用本质上都是异步的,将所有异步调用包装到Task&lt; T&gt;中似乎是合适的。对象。以下是本机库结构的简化示例:

delegate void MyCallback(string outputData);

class MyNativeLibrary
{
    public int RegisterCallback(MyCallback callback); // returns -1 on error
    public int RequestData(string inputData);         // returns -1 on error
}

现在,我已通过事件订阅提供了返回值,但我相信这将是一种更好的方式来返回我的数据:

class WrapperAroundNativeCode
{
    public async Task<string> RequestData(string inputData);
}

到目前为止,我一直未能找到适当的方法来实现这一点,而且我正在与那些在使用Task&lt; T&gt;的工作方面有更多经验的人们联系。对象和async / await模式比我做的。

2 个答案:

答案 0 :(得分:9)

您可以使用TaskCompletionSource<TResult>。以下代码的内容:

class WrapperAroundNativeCode
{
    public async Task<string> RequestData(string inputData)
    {
        var completionSource = new TaskCompletionSource<string>();
        var result = Native.RegisterCallback(s => completionSource.SetResult(s));
        if(result == -1)
        {
            completionSource.SetException(new SomeException("Failed to set callback"));
            return completionSource.Task;
        }

        result = Native.RequestData(inputData);
        if(result == -1)
            completionSource.SetException(new SomeException("Failed to request data"));
        return completionSource.Task;
    }
}

此答案假定不会同时调用此方法。如果有,你需要一些方法来区分不同的电话。许多API提供userData有效负载,您可以将其设置为每次调用的唯一值,以便您可以区分。

答案 1 :(得分:6)

听起来你正在寻找TaskCompletionSource<T>。您可以通过创建TaskCompletionSource来包装库,创建MyNativeLibrary的实例并注册设置任务完成源结果的回调,然后从同一实例请求数据。如果其中任何一个步骤失败,请在任务完成源上设置错误。然后只需将TaskCompletionSource<>.Task属性的值返回给调用者。

(假设您可以创建MyNativeLibrary的单独实例 - 如果您只能在整个应用中创建单个实例,则会变得更加困难。)