【发布时间】:2012-06-28 01:07:24
【问题描述】:
我有一个进程,我需要对服务器进行大约 100 次 http api 调用并处理结果。我已经把这个命令执行器放在一起,它构建了一个命令列表,然后异步运行它们。进行大约 100 次调用并解析结果需要超过 1 分钟。使用浏览器的 1 个请求会在大约 100 毫秒内给我一个响应。你会认为大约 100 次调用大约需要 10 秒。我相信我做错了什么,这应该会更快。
public static class CommandExecutor
{
private static readonly ThreadLocal<List<Command>> CommandsToExecute =
new ThreadLocal<List<Command>>(() => new List<Command>());
private static readonly ThreadLocal<List<Task<List<Candidate>>>> Tasks =
new ThreadLocal<List<Task<List<Candidate>>>>(() => new List<Task<List<Candidate>>>());
public static void ExecuteLater(Command command)
{
CommandsToExecute.Value.Add(command);
}
public static void StartExecuting()
{
foreach (var command in CommandsToExecute.Value)
{
Tasks.Value.Add(Task.Factory.StartNew<List<Candidate>>(command.GetResult));
}
Task.WaitAll(Tasks.Value.ToArray());
}
public static List<Candidate> Result()
{
return Tasks.Value.Where(x => x.Result != null)
.SelectMany(x => x.Result)
.ToList();
}
}
我传入此列表的命令创建了一个新的 httpclient,使用 url 在该客户端上调用 getasync,将字符串响应转换为对象,然后对字段进行水合。
protected void Initialize()
{
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
}
protected override void Execute()
{
Initialize();
var task = _httpClient.GetAsync(string.Format(Url, Input));
Result = ConvertResponseToObjectAsync(task).Result;
Result.ForEach(x => x.prop = value);
}
private static Task<Model> ConvertResponseToObjectAsync(Task<HttpResponseMessage> task)
{
return task.Result.Content.ReadAsAsync<Model>(
new MediaTypeFormatter[]
{
new Formatter()
});
}
您能否了解我的瓶颈或对如何加快速度有任何建议。
编辑 进行这些更改使其缩短到 4 秒。
protected override void Execute()
{
Initialize();
_httpClient.GetAsync(string.Format(Url, Input))
.ContinueWith(httpResponse => ConvertResponseToObjectAsync(httpResponse)
.ContinueWith(ProcessResult));
}
protected void ProcessResult(Task<Model> model)
{
Result = model.Result;
Result.ForEach(x => x.prop = value);
}
【问题讨论】:
-
您进行了测量吗?每个请求的执行速度有多快?大部分时间都花在了哪里?所有请求都发往同一台服务器吗?
-
还要检查您实际能够发出多少并行请求。有一些默认限制可能会限制吞吐量。
-
dotTrace(我是新手)说 63% 在 system.threading.monitor.wait 上。我将添加一些调试打印语句以查看每个语句的执行速度。我已经对代码进行了一些更改,如果没有帮助,我会更新问题以反映。
-
您阻止任务完成而不是执行 ContinueWith(或使用 async/await),无论是在 ConvertResponseToObjectAsync(将阻止直到输入任务完成)和 Execute(将在返回的任务完成时阻止) .您通常会“等待”它们,或者如果您不能使用 await,则使用 ContinueWith,这样您就不会阻止等待任务完成。
-
感谢@JamesManning 我将它固定在其中一个位置。你能评论我添加的编辑吗?
标签: c# optimization task-parallel-library asp.net-web-api