2

我有一个 API,其界面如下所示:

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

实现此方法的最佳方法是什么?

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

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

4

2 回答 2

4

编辑

如果您想在不阻塞线程的情况下执行此操作,可以使用以下方法执行以下操作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;

原答案:

我认为您想要类似以下的内容,它使用 aManualResetEvent来阻止线程,直到 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

于 2012-07-11T18:30:29.240 回答
0

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

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;
    });
}
于 2012-07-11T18:36:57.713 回答