我之前从未使用async
方法,但我发现自己构建了一个监视工具,并认为我可以使用这种方法来加快速度。
该工具定义了 SiteConfigurations 列表,每个 SiteConfiguration 都有一个待测试的监视器列表(通过HTTP请求)。该工具的体系结构基于管道,例如:
MonitorSuccessPipeline
MonitorFailurePipeline
SiteAfterMonitorsPipeline
将被执行EndEnginePipeline
将被执行最初的方法是循环遍历所有SiteConfigurations
,并为每个Monitors
运行所有关联的RunMonitor
。
总执行时间:100个SiteConfigurations x 4监视器每个监视器x~1秒= ~400秒运行。
所以我将async
更改为 private async Task<Result> RunMonitor(Monitor currentMonitor)
{
Result result = new Result();
var client = new HttpClient();
var response = client.GetAsync(currentMonitor.TestUrl);
var content = await response.Result.Content.ReadAsStringAsync();
bool containSuccessText = content.Contains(currentMonitor.SuccessText);
bool isOk = status == HttpStatusCode.OK;
result.Success = containSuccessText && isOk;
if (result.Success)
{
ExecutePipeline("MonitorSuccessPipeline", currentMonitor);
}
else
{
ExecutePipeline("MonitorFailurePipeline", currentMonitor);
}
return result;
}
(实际方法比您在此处看到的更多,这是&#34;核心&#34;方法):
RunSiteConfigurationMonitors
接下来,我将 private async Task<Result> RunSiteConfigurationMonitors(SiteConfiguration siteConfig)
{
Result result = new Result();
List<Task<Result>> tasks = new List<Task<Result>>();
foreach (var currentMonitor in siteConfig.Monitors)
{
tasks.Add(RunMonitor(siteConfig, globalConfig, currentMonitor));
}
var results = await Task.WhenAll(tasks);
foreach (var r in results)
{
result.Insert(r.Body);
}
ExecutePipeline("SiteAfterMonitorsPipeline", siteConfig);
return result;
}
方法更改为:
RunEngine
最后,我将 public async Task RunEngine(string configName)
{
GlobalConfiguration globalConfig = GetConfiguration(configName);
List<Task<Result>> tasks = new List<Task<Result>>();
foreach (var sc in globalConfig.SiteConfigurations)
{
tasks.Add(RunSiteConfigurationMonitors(sc, globalConfig));
}
var results = await Task.WhenAll(tasks);
foreach (var r in results)
{
logger.Insert(r.Body);
}
ExecutePipeline("EndEnginePipeline", globalConfig);
}
方法更改为:
RunEngine
我希望看到RunSiteConfigurationMonitors
方法调用所有RunSiteConfigurationMonitors
并看到它们同时并行运行 - 然后每个RunMonitor
调用[21:56:50.419]: Loading configuration...
[21:56:58.480]: [21:56:52.711]: Monitoring site A
[21:56:58.583]: [21:56:53.954]: [21:56:52.753]: Found monitor: A1
[21:56:58.687]: [21:56:53.954]: [21:56:52.753]: Testing URL: http://a1.example.com
[21:56:58.791]: [21:56:53.954]: [21:56:53.106]: -- Contains success text? Yes
[21:56:58.894]: [21:56:53.954]: [21:56:53.106]: -- Status Code: OK
[21:56:58.997]: [21:56:53.954]: [21:56:53.106]: -- Success? True
[21:56:59.100]: [21:56:53.954]:
[21:56:59.203]: [21:56:53.954]: [21:56:53.474]: Found monitor: A2
[21:56:59.306]: [21:56:53.954]: [21:56:53.474]: Testing URL: http://a2.example.com
[21:56:59.375]: [21:56:53.954]: [21:56:53.761]: -- Contains success text? Yes
[21:56:59.478]: [21:56:53.954]: [21:56:53.762]: -- Status Code: OK
[21:56:59.582]: [21:56:53.954]: [21:56:53.762]: -- Success? True
[21:56:59.686]: [21:56:53.954]:
[21:56:59.790]:
[21:56:59.894]: [21:56:54.126]: Monitoring site: B
[21:56:59.998]: [21:56:56.424]: [21:56:54.126]: Found monitor: B1
[21:57:00.101]: [21:56:56.424]: [21:56:54.126]: Testing URL: http://b1.example.com
[21:57:00.204]: [21:56:56.424]: [21:56:55.225]: -- Contains success text? Yes
[21:57:00.307]: [21:56:56.424]: [21:56:55.225]: -- Status Code: OK
[21:57:00.410]: [21:56:56.424]: [21:56:55.225]: -- Success? True
[21:57:00.515]: [21:56:56.424]:
[21:57:00.619]: [21:56:56.424]: [21:56:55.428]: Found monitor: B2
[21:57:00.724]: [21:56:56.424]: [21:56:55.429]: Testing URL: http://b2.example.com
[21:57:00.827]: [21:56:56.424]: [21:56:56.254]: -- Contains success text? Yes
[21:57:00.931]: [21:56:56.424]: [21:56:56.254]: -- Status Code: OK
[21:57:01.036]: [21:56:56.424]: [21:56:56.254]: -- Success? True
[21:57:01.140]: [21:56:56.424]:
[21:57:01.244]:
[21:57:01.348]: [21:56:56.597]: Monitoring site: C
[21:57:01.452]: [21:56:58.206]: [21:56:56.597]: Found monitor: C1
[21:57:01.557]: [21:56:58.206]: [21:56:56.597]: Testing URL: http://c1.example.com
[21:57:01.662]: [21:56:58.206]: [21:56:57.219]: -- Contains success text? Yes
[21:57:01.766]: [21:56:58.206]: [21:56:57.219]: -- Status Code: OK
[21:57:01.869]: [21:56:58.206]: [21:56:57.219]: -- Success? True
[21:57:01.974]: [21:56:58.206]:
[21:57:02.078]: [21:56:58.206]: [21:56:57.418]: Found monitor: C2
[21:57:02.182]: [21:56:58.206]: [21:56:57.418]: Testing URL: http://c2.example.com
[21:57:02.287]: [21:56:58.206]: [21:56:58.025]: -- Contains success text? Yes
[21:57:02.392]: [21:56:58.206]: [21:56:58.025]: -- Status Code: OK
[21:57:02.496]: [21:56:58.206]: [21:56:58.025]: -- Success? True
[21:57:02.602]: [21:56:58.206]:
[21:57:02.706]:
和看那些同时运行。
相反,这就是监视器子集的输出:
RunMonitor
正如您所看到的,监视器基本上按顺序运行,并且站点配置也未在&#34; parallel&#34;中运行。
我的期望是错误的,还是我的代码有问题?就像我说的,这对我来说是个新世界,因此如果可能的话,我会真的欣赏一个简单的解释。
解
根据Stephen Cleary的建议,我将 private async Task<Result> RunMonitor(Monitor currentMonitor)
{
Result result = new Result();
var client = new HttpClient();
var response = await client.GetAsync(currentMonitor.TestUrl);
var content = await response.Content.ReadAsStringAsync();
var status = response.StatusCode;
bool containSuccessText = content.Contains(currentMonitor.SuccessText);
bool isOk = status == HttpStatusCode.OK;
result.Success = containSuccessText && isOk;
if (result.Success)
{
ExecutePipeline("MonitorSuccessPipeline", currentMonitor);
}
else
{
ExecutePipeline("MonitorFailurePipeline", currentMonitor);
}
return result;
}
更改为:
[23:33:44.199]: Loading configuration...
[23:33:48.968]: [23:33:46.601]: Monitoring site A
[23:33:49.073]: [23:33:47.637]: [23:33:46.625]: Found monitor: A1
[23:33:49.176]: [23:33:47.637]: [23:33:46.625]: Testing URL: http://a1.example.com
[23:33:49.281]: [23:33:47.637]: [23:33:47.047]: -- Contains success text? Yes
[23:33:49.386]: [23:33:47.637]: [23:33:47.047]: -- Status Code: OK
[23:33:49.490]: [23:33:47.637]: [23:33:47.047]: -- Success? True
[23:33:49.594]: [23:33:47.637]:
[23:33:49.667]: [23:33:47.637]: [23:33:46.692]: Found monitor: A2
[23:33:49.770]: [23:33:47.637]: [23:33:46.692]: Testing URL: http://a2.example.com
[23:33:49.874]: [23:33:47.637]: [23:33:47.461]: -- Contains success text? Yes
[23:33:49.978]: [23:33:47.637]: [23:33:47.461]: -- Status Code: OK
[23:33:50.082]: [23:33:47.637]: [23:33:47.461]: -- Success? True
[23:33:50.186]: [23:33:47.637]:
[23:33:50.292]:
[23:33:50.396]: [23:33:46.727]: Monitoring site B
[23:33:50.500]: [23:33:48.690]: [23:33:46.727]: Found monitor: B1
[23:33:50.604]: [23:33:48.690]: [23:33:46.727]: Testing URL: http://b1.example.com
[23:33:50.708]: [23:33:48.690]: [23:33:48.547]: -- Contains success text? Yes
[23:33:50.812]: [23:33:48.690]: [23:33:48.547]: -- Status Code: OK
[23:33:50.915]: [23:33:48.690]: [23:33:48.547]: -- Success? True
[23:33:51.019]: [23:33:48.690]:
[23:33:51.124]: [23:33:48.690]: [23:33:46.727]: Found monitor: B2
[23:33:51.228]: [23:33:48.690]: [23:33:46.727]: Testing URL: http://b2.example.com
[23:33:51.332]: [23:33:48.690]: [23:33:48.336]: -- Contains success text? Yes
[23:33:51.437]: [23:33:48.690]: [23:33:48.336]: -- Status Code: OK
[23:33:51.541]: [23:33:48.690]: [23:33:48.336]: -- Success? True
[23:33:51.645]: [23:33:48.690]:
[23:33:51.749]:
[23:33:51.852]: [23:33:46.728]: Monitoring site C
[23:33:51.956]: [23:33:48.161]: [23:33:46.728]: Found monitor: C1
[23:33:52.060]: [23:33:48.161]: [23:33:46.728]: Testing URL: http://c1.example.com
[23:33:52.165]: [23:33:48.161]: [23:33:47.813]: -- Contains success text? Yes
[23:33:52.271]: [23:33:48.161]: [23:33:47.813]: -- Status Code: OK
[23:33:52.375]: [23:33:48.161]: [23:33:47.813]: -- Success? True
[23:33:52.479]: [23:33:48.161]:
[23:33:52.583]: [23:33:48.161]: [23:33:46.760]: Found monitor: C2
[23:33:52.688]: [23:33:48.161]: [23:33:46.760]: Testing URL: http://c2.example.com
[23:33:52.793]: [23:33:48.161]: [23:33:47.987]: -- Contains success text? Yes
[23:33:52.897]: [23:33:48.161]: [23:33:47.987]: -- Status Code: OK
[23:33:53.001]: [23:33:48.161]: [23:33:47.987]: -- Success? True
[23:33:53.105]: [23:33:48.161]:
[23:33:53.208]:
现在它正在并行工作!
{{1}}但是,我必须承认,我并不真正理解为什么这有效。
答案 0 :(得分:8)
异步编程的一个指导原则是不阻止异步代码。请考虑RunMonitor
中的此代码:
var response = client.GetAsync(currentMonitor.TestUrl);
var content = await response.Result.Content.ReadAsStringAsync();
response
的类型是一项任务,下一行访问Task<T>.Result
,同步阻止该方法,直到HTTP请求完成。所以这个方法不是异步操作的。你可能想要的是:
var response = await client.GetAsync(currentMonitor.TestUrl);
var content = await response.Content.ReadAsStringAsync();
答案 1 :(得分:1)
评论通常是正确的。任务的调度取决于运行时,无论是Web应用程序,控制台应用程序等。或者,如果您的应用程序只是一个按计划执行的监视工具,您可以使用Parallel.ForEach()
方法集您的RunEngine()
和RunSiteConfigurationMonitors
而不是等待任务。您的每个RunMonitor
都会将结果写入ConcurrentBag<>
。使用Parallel.ForEach
,您还可以通过ParallelOptions
控制并行度,但同样,也不会超过调度程序提供的线程数。希望这会有所帮助。