我有一个API,其界面如下所示:
void SendRequest(Guid id, IRequest request);
event EventHandler<ResponseEventArgs> ResponseReceived;
实施此方法的最佳方法是什么?
Task<T> GetResponse(IRequest request) where T: IRequest
请注意,多个请求可能会相互重叠,因此当响应返回时,我需要查找父请求。我有一种感觉,TaskCompletionSource可能有用,但不能把它拼凑在一起。
答案 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;
});
}