【发布时间】:2017-06-23 02:16:54
【问题描述】:
我了解异步 javascript,但 aync .NET 有不同的方法,我仍然没有正确理解它。
我有一个要检查的 URL 列表。我想异步检查它们并获取第一个返回特定状态码的。在这种情况下,我正在寻找状态代码 401(未授权),因为这表明这是一个登录挑战,这是我所期望的。所以我不能只使用Task.WaitAny,因为我需要运行一些代码来查看哪个首先与我的状态码匹配。
谁能给我一个示例,说明如何在 aync 任务上运行回调,然后在找到所需内容时停止所有其他任务?
我在这个项目中使用 .NET 4,如果可能的话,我更愿意坚持使用它。我安装了System.Net.Http.HttpClient nuget 包。
更新:
我已经整理了以下代码,我终于得到了正确的结果,除了我认为它正在等待每个任务 - 错过了异步的全部意义。不确定在内部任务中使用new Task() 或t.Wait(),但它似乎是捕获异常的唯一方法。 (DNS 失败和连接超时时会发生异常 - 我不知道有比捕获和忽略异常更好的方法来处理这些问题。)
关于改进此代码以使其真正异步的任何建议?
public async Task<ActionResult> Test() {
//var patterns = GetPatterns();
var patterns = "http://stackoverflow.com/,https://www.google.com,http://www.beweb.co.nz,https://outlook.office365.com/Microsoft-Server-ActiveSync,http://rubishnotexist.com".Split(",").ToList();
var httpClient = new System.Net.Http.HttpClient();
string result = "";
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken cancellationToken = source.Token;
var allTasks = new List<Task>();
foreach (var pattern in patterns) {
var url = pattern;
Task task = new Task(() => {
string answer = "";
var st = DateTime.Now;
var t = httpClient.GetAsync(pattern, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
t.ContinueWith(d => {
if (!source.IsCancellationRequested) {
if (t.IsFaulted) {
answer = "Fault - " + " " + url;
} else if (d.Result.StatusCode == System.Net.HttpStatusCode.Unauthorized) {
// found it - so cancel all others
answer = "YES - " + d.Result.StatusCode + " " + url;
//source.Cancel();
} else {
answer = "No - " + d.Result.StatusCode + " " + url;
}
}
result += answer + " ("+(DateTime.Now-st).TotalMilliseconds+"ms)<br>";
});
try {
t.Wait();
} catch (Exception) {
// ignore eg DNS fail and connection timeouts
}
});
allTasks.Add(task);
task.Start();
}
// Wait asynchronously for all of them to finish
Task.WaitAll(allTasks.ToArray());
return Content(result + "<br>DONE");
}
在上面我没有取消部分工作。这是一个包含取消的版本:
public async Task<ActionResult> Test2(string email) {
var patterns = GetPatterns(email);
patterns = "http://stackoverflow.com/,https://www.google.com,http://www.beweb.co.nz,https://outlook.office365.com/Microsoft-Server-ActiveSync,http://rubishnotexist.com".Split(",").ToList();
var httpClient = new System.Net.Http.HttpClient();
string result = "";
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken cancellationToken = source.Token;
var allTasks = new List<Task>();
foreach (var pattern in patterns) {
var url = pattern;
Task task = new Task(() => {
string answer = "";
var st = DateTime.Now;
var t = httpClient.GetAsync(pattern, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
t.ContinueWith(d => {
if (!source.IsCancellationRequested) {
if (t.IsFaulted) {
answer = "Fault - " + " " + url;
} else if (d.Result.StatusCode == System.Net.HttpStatusCode.Unauthorized) {
// found it - so cancel all others
answer = "YES - " + d.Result.StatusCode + " " + url;
result += answer + " (" + (DateTime.Now - st).TotalMilliseconds + "ms) <-- cancelled here <br>";
source.Cancel();
} else {
answer = "No - " + d.Result.StatusCode + " " + url;
}
} else {
answer = "cancelled - " + url;
}
result += answer + " (" + (DateTime.Now - st).TotalMilliseconds + "ms)<br>";
});
try {
t.Wait();
} catch (Exception) {
// ignore
}
});
allTasks.Add(task);
task.Start();
}
// Wait asynchronously for all of them to finish
Task.WaitAll(allTasks.ToArray());
return Content(result + "<br>DONE");
}
【问题讨论】:
-
只使用
Task.WhenAll()怎么样,然后检查任务的结果(假设您在返回值中包含 HTTP 响应状态代码)? -
我会尝试 Task.WhenAll 但有些请求可能需要很长时间,所以我想在找到一个请求时退出并停止其他请求。
-
您可以创建一个
CancellationToken,将其传递给所有任务,并在第一次失败时取消它。然后所有任务都会观察令牌(通过传递给支持它的异步方法,或者通过偶尔显式检查它),并在它被取消时退出。 -
所以我会使用带有取消令牌的
Task.WhenAll()? -
不,您需要将
CancellationToken传递给所有任务。 This answer有详细解释。
标签: c# asp.net asp.net-mvc asynchronous async-await