我刚刚开始处理任务。我们有一个使用请求/响应的系统设置。运行任务的服务接受具有请求对象列表的主请求,并返回具有响应对象列表的主响应。所以它看起来像这样
var MasterRequest = new MasterRequest;
MasterRequest.Requests.Add(new BlueRequest);
MasterRequest.Requests.Add(new RedRequest);
MasterRequest.Requests.Add(new YellowRequest);
请求实现了一个简单的IRequest接口,每种颜色都是一个具体的类。该服务具有具体类(请求处理器),其被设置为能够根据具体请求对象单独地并且同时地处理每个请求。服务上的每个具体类都有一个GetTask方法,其签名如下:
Task<IResponse> GetTask(IRequest);
{
// some setup stuff
return Task.Factory.StartNew<IResponse>(() =>
{
// do task stuff
return response; // implements IResponse
});
}
我的服务接受传入的MasterRequest,并通过调用具体请求处理器上面列出的GetTask调用来构建任务列表。然后我使用列表上的Parallel.ForEach来处理任务。
// this is what is returned from the service.
// it has a List<IResponse> on it to hold the resposnes
MasterResposne resposne = new MasterResponse();
List<Task<IResponse>> tasks = new List<Task<IResponse>>();
foreach(IRequest req in MasterRequest.Requests)
{
// factory to get the proper request processor
RequestProcessor p = rp.GetProcessor(req);
tasks.add(p.GetTask(req));
}
Parallel.ForEach(tasks, t =>
{
t.Wait();
// check for faulted and cancelled
// this is where I need help
response.Responses.Add(t.Result);
}
这一切都很棒。但是如果任务抛出异常,我不知道如何将其绑定到触发它的特定具体请求。我需要知道,所以我可以将正确构建的响应传回给调用者。
我的第一个想法是将Task子类化,但是它提出了我自己不想处理的一系列问题。
我读过这篇SO文章,好像我想做这样的事情
Is this ok to derive from TPL Task to return more details from method?
我认为Reed的第二个例子是我的解决方案,但我仍然无法看到如何同时运行任务,并且能够将异常与请求联系起来,以便我可以返回正确构建的响应列表。
提前致谢。
答案 0 :(得分:0)
所以我能够从我提供的链接中使用Reed的解决方案。我处理请求的服务代码变成了这个
// this is what is returned from the service.
// it has a List<IResponse> on it to hold the resposnes
MasterResposne resposne = new MasterResponse();
List<ExecutionResult> tasks = new List<ExecutionResult>();
foreach(IRequest req in MasterRequest.Requests)
{
// factory to get the proper request processor
RequestProcessor p = rp.GetProcessor(req);
tasks.add(p.GetResult(req));
}
Parallel.ForEach(tasks, t =>
{
t.task.Wait();
response.Responses.Add(t.Result);
}
其中ExecutionResult定义如此
class ExecutionResult
{
public IResult Result;
public Task<IResponse> task;
}
这使我可以访问预先构建的响应对象,以便将其传递给调用者。
修改强>: 所以我查看了我的Parallel.ForEach并能够重做我的代码并按照建议使用等待Task.WhenAll。新代码看起来更像是这样:
// this is what is returned from the service.
// it has a List<IResponse> on it to hold the resposnes
MasterResposne resposne = new MasterResponse();
List<ExecutionResult> tasks = new List<ExecutionResult>();
List<ExecutionResult> executionResults = new List<ExecutionResult>();
foreach(IRequest req in MasterRequest.Requests)
{
// factory to get the proper request processor
RequestProcessor p = rp.GetProcessor(req);
ExecutionResult er = engine.GetResult(req);
executionResults.Add(er);
tasks.Add(er.Task);
}
await Task.WhenAll<IResponse>(tasks);
foreach (ExecutionResult r in executionResults)
{
if (r.Task.IsCompleted)
{
response.AddResponse(r.Task.Result);
}
else
{
r.Response.Status = false;
AggregateException flat = r.Task.Exception.Flatten();
foreach (Exception e in flat.InnerExceptions)
{
Log.ErrorFormat("Reqest [{0}] threw [{1}]", r.Response.RequestId, e);
r.Response.StatusReason.AppendLine(e.Message);
}
}
}
这允许我将我的请求信息绑定到我的任务并获得我需要返回给我的调用者的响应。
感谢您的指导。
答案 1 :(得分:0)
然后我在列表上使用Parallel.ForEach来处理任务。
这实际上非常糟糕。它将一个吨的线程投入到混合中,只是为了阻止完成任务。
但是如果任务抛出异常,我不知道如何将其绑定回触发它的特定具体请求。我需要知道,所以我可以将正确构建的响应传回给调用者。
每当你有“完成后的流程任务”这类问题时,通常最好的解决方案是更高级别的异步操作:
private async Task<IResponse> ProcessAsync(IRequest request)
{
try
{
return await engine.GetResult(request);
}
catch (Exception ex)
{
IResponse result = /* create error response */;
return result;
}
}
这允许更简单的主要功能:
MasterResposne resposne = new MasterResponse();
var tasks = MasterRequest.Requests.Select(req => ProcessAsync(req));
response.AddRange(await Task.WhenAll(tasks));