实现远程调用的任务<t>而不是在事件中响应?</t>

时间:2012-07-11 18:14:43

标签: .net task-parallel-library

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

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

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

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

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

2 个答案:

答案 0 :(得分: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;

原始答案:

我认为您需要类似以下的内容,它使用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

答案 1 :(得分: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;
    });
}