最初我使用Threads和ThreadPooling编写了我的C#程序,但是这里的大多数用户都告诉我Async是一种更好的方法来提高效率。我的程序将JSON对象发送到服务器,直到返回状态代码200,然后继续执行下一个任务。
问题是,一旦我的一个任务检索到200的状态代码,它等待其他任务获得200个代码,然后转到下一个任务。我希望每项任务都能继续执行下一项任务,而无需等待其他任务完成(或赶上)200响应。
以下是我并行运行任务的主要课程。
public static void Main (string[] args)
{
var tasks = new List<Task> ();
for (int i = 0; i < 10; i++) {
tasks.Add (getItemAsync (i));
}
Task.WhenAny (tasks);
}
这是getItemAsync()方法,它实际向另一台服务器发送信息。在此方法有效之前,它需要一个密钥。问题还在于,让我说我运行100个任务,所有100个任务都将等到每个任务都有一个密钥。
public static async Task getItemAsync (int i)
{
if (key == "") {
await getProductKey (i).ConfigureAwait(false);
}
Uri url = new Uri ("http://www.website.com/");
var content = new FormUrlEncodedContent (new[] {
...
});
while (!success) {
using (HttpResponseMessage result = await client.PostAsync (url, content)) {
if (result.IsSuccessStatusCode) {
string resultContent = await result.Content.ReadAsStringAsync ().ConfigureAwait(false);
Console.WriteLine(resultContent);
success=true;
}
}
}
await doMoreAsync (i);
}
这里是检索密钥的函数,它使用HttpClient并进行解析。
public static async Task getProductKey (int i)
{
string url = "http://www.website.com/key";
var stream = await client.GetStringAsync (url).ConfigureAwait(false);
var doc = new HtmlDocument ();
doc.LoadHtml (stream);
HtmlNode node = doc.DocumentNode.SelectNodes ("//input[@name='key']") [0];
try {
key = node.Attributes ["value"].Value;
} catch (Exception e) {
Console.WriteLine ("Exception: " + e);
}
}
每个任务收到一个密钥并具有200个状态代码后,它将运行doMoreAsync()。我希望检索200代码的任何单个Task都能运行doMoreAsync()而无需等待其他任务赶上。我该怎么做?
答案 0 :(得分:0)
您的主要方法不是等待任务完成,它只会触发一堆异步任务并返回。
如果要等待异步任务,则只能通过异步方法执行此操作。解决方法是从Main方法启动异步任务,并使用阻止Task.Wait()
等待其完成:
public static void Main(string[] args) {
Task.Run(async () =>
{
var tasks = new List<Task> ();
for (int i = 0; i < 10; i++) {
tasks.Add (getItemAsync (i));
}
var finishedTask = await Task.WhenAny(tasks); // This awaits one task
}).Wait();
}
当您从异步方法调用getItemAsync()
时,您也可以删除ConfigureAwait(false)
。 ConfigureAwait(false)
只是确保某些代码不会在UI线程上执行。
如果要将其他任务附加到任务,您还可以使用ContinueWith()
直接将其附加到上一个任务:
getItemAsync(i).ContinueWith(anotherTask);
答案 1 :(得分:0)
您的主要问题似乎是您在所有同时运行的异步操作中共享key
字段。这将不可避免地导致种族危害。相反,您应该将getProductKey
方法更改为返回每个检索到的键作为其异步操作的结果:
// ↓ result type of asynchronous operation
public static async Task<string> getProductKey(int i)
{
// retrieval logic here
// return key as result of asynchronous operation
return node.Attributes["value"].Value;
}
然后,你就这样消费它:
public static async Task getItemAsync(int i)
{
string key;
try
{
key = await getProductKey(i).ConfigureAwait(false);
}
catch
{
// handle exceptions
}
// use local variable 'key' in further asynchronous operations
}
最后,在您的主逻辑中,使用WaitAll
来阻止控制台应用程序在所有任务完成之前终止。虽然WaitAll
是阻塞调用,但在这种情况下可以接受,因为您希望主线程被阻塞。
public static void Main (string[] args)
{
var tasks = new List<Task> ();
for (int i = 0; i < 10; i++) {
tasks.Add(getItemAsync(i));
}
Task.WaitAll(tasks.ToArray());
}