2016-03-05 66 views
0

本来我写了使用线程和ThreadPooling我的C#程序,但大多数在这里的用户都告诉我异步是为了提高效率的更好方法。我的程序将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

你的主要方法是不是等待任务来完成,它甫一火灾一串的异步任务并返回。 如果你想等待一个异步任务,你只能做的是从异步方法。解决方法是从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); 
+0

感谢您的回复,我几分钟前发现了ContinueWith,现在我正在测试该方法。它不断返回相同的线程#。和var finishedTask =等待tasks.WhenAny()不起作用。它说“System.Collections.Generic.list”不包含“whenany”的定义。 –

+0

我的意思是'等待Task.WhenAny(任务);'这将等待列表中的一个任务完成。您可以在完成后从列表中删除该任务。 另外任务不会映射到线程1:1。一堆任务可以在同一个线程内执行。任务可以在另一个人等待某件事情时执行。 –

+0

是的,它不在我的主要工作。它扔我system.collections.generic.list错误。 –

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()); 
} 
+0

是的,我尝试了WaitAll()方法,但它不断给我这个奇怪的错误,说:“参数#1无法转换'system.collections.generic.list '表达式键入'系统。 threading.task.tasks []“ –

+0

@CO:我在'tasks'列表中缺少'.ToArray()'调用。尝试更新的版本。 – Douglas

+0

是的,我早就明白这一点了。它仍然不能独立工作,但程序不会很快关闭。它仍在等待所有的任务检索那里的密钥,以继续下一步。 –