为<T>远程呼叫实现任务而不是在事件中响应?



我有一个API,它的接口如下:

void SendRequest(Guid id, IRequest request);
event EventHandler<ResponseEventArgs> ResponseReceived;

实现这种方法的最佳方式是什么?

Task<T> GetResponse(IRequest request) where T: IRequest

请注意,多个请求可能相互重叠,因此当返回响应时,我需要查找父请求。我有一种感觉TaskCompletionSource可能有用,但不能完全拼凑在一起。

编辑

如果你想在不阻塞线程的情况下做到这一点,你可以使用TaskCompletionSource<T>:来做这样的事情

var completionSource = new TaskCompletionSource<T>();
var requestIdentifier = Guid.NewGuid();
EventHandler<ResponseEventArgs> handler = null;
handler = (sender, args) =>
{
    if(args.RequestIdentifier == requestIdentifier)
    {
        api.ResponseReceived -= handler; 
        // TrySetResult avoids re-entrancy problems in case of an
        // API that sends duplicates, but there other ways of 
        // dealing with this too.
        completionSource.TrySetResult((T)args.Response);
    }      
};

api.ResponseReceived += handler; 
// Make this async if you want.
api.SendRequest(requestIdentifier, request); 
return completionSource.Task;

原始答案:

我认为您需要类似以下的东西,它使用ManualResetEvent来阻止线程,直到API引发事件:

return Task.Factory.StartNew<T>(() =>
{
    var waitHandle = new ManualResetEvent(false);
    T result = default(T);
    var requestIdentifier = Guid.NewGuid();
    EventHandler<ResponseEventArgs> handler = (sender, args) =>
    {
         if(args.RequestIdentifier == requestIdentifier)
         {
             result = (T)args.Response; // Not sure how this looks in your API
             waitHandle.Set(); // Unblock the thread running the task     
         }      
    };
    // Attach handler to respond to the response being received.
    api.ResponseReceived += handler; 
    // Send request off.
    api.SendRequest(requestIdentifier, request); 
    // Wait until response is received.
    waitHandle.WaitOne(); 
    // Detach handler to prevent leak.
    api.ResponseReceived -= handler; 
    return result;
});

要获得更干净的方法,请查看Reactive Extensions。

您可以返回等待事件返回的任务:

Task<T> GetResponse<T>() 
{
    T result = default(T);
    ManualResetEvent ev = new ManualResetEvent(false);
    ResponseReceived += (s,e) =>  {     
        result = default(T); /* result = e.Xxx */
        ev.Set();
    };
    return new Task<T>(() => {
        SendRequest(Guid.NewGuid());        
        ev.WaitOne();
        return result;
    });
}

最新更新