2013-06-25 97 views
2

我有以下情況(或與異步await機制的基本誤解)。創建一個.net異步包裝到同步請求

假設您有一組需要很長時間的1-20 Web請求呼叫:findItemsByProduct()。 你想包裝它在一個異步請求,這將能夠將所有這些調用抽象爲一個異步調用,但我似乎無法做到這一點,而不使用更多的線程。

如果我這樣做:這裏

int total = result.paginationOutput.totalPages; 
for (int i = 2; i < total + 1; i++) 
    { 

     await Task.Factory.StartNew(() => 
     { 
     result = client.findItemsByProduct(i); 
     }); 
     newList.AddRange(result.searchResult.item); 

     } 
    } 
return newList; 

問題,該電話不在一起運行,而他們是由一個等待之一。 我希望所有的電話一起運行,並收穫結果。

僞代碼,我想代碼可以這樣運行:

forEach item { 
    result = item.makeWebRequest(); 
} 
foreach item { 
    List.addRange(item.harvestResults); 
} 

我不知道如何使代碼要做到這一點,但..

+0

你從哪裏得到了使用'Task.Factory.StartNew'的想法? –

+0

您的數據訪問組件是否具有可用於異步實現'findItemsByProduct'的任何異步方法? –

+0

如果你想同時運行多個操作(併發),你真的要問怎麼並行運行**,這是異步代碼的一個不同的(儘管是互補的)想法。 –

回答

1

考慮你的要求,我見爲:

  • 過程ñ的非阻塞任務數
  • 處理結果所有查詢都返回

我會用CountdownEvent此例如

var results = new ConcurrentBag<ItemType>(result.pagination.totalPages); 
using (var e = new CountdownEvent(result.pagination.totalPages)) 
{ 
    for (int i = 2; i <= result.pagination.totalPages+1; i++) 
    { 
     Task.Factory.StartNew(() => return client.findItemsByProduct(i)) 
        .ContinueWith(items => { 
         results.AddRange(items); 
         e.Signal(); // signal task is done 
        }); 
    } 
    // Wait for all requests to complete 
    e.Wait(); 
} 
// Process results 
foreach (var item in results) 
{ 
    ... 
} 
+1

對於那些不能使用異步和等待語言功能的人來說,這是一個很好的答案。 –

0

即使沒有使用await也可以很容易地解決這個問題。只需創建每個任務,把所有的任務到一個列表,然後在名單上使用WhenAll來獲取表示所有這些任務的完成一個任務:

public static Task<Item[]> Foo() 
{ 
    int total = result.paginationOutput.totalPages; 

    var tasks = new List<Task<Item>>(); 

    for (int i = 2; i < total + 1; i++) 
    { 
     tasks.Add(Task.Factory.StartNew(() => client.findItemsByProduct(i))); 
    } 

    return Task.WhenAll(tasks); 
} 

還要注意你有一個大您在代碼中如何使用result的問題。你有不同的任務都使用相同的變量,所以有競爭條件,它是否正常工作。您最終可能會添加兩次相同的呼叫,並且完全跳過一次。相反,您應該致電findItemsByProduct作爲任務的結果,並使用該任務的Result

1

理想情況下,您應該添加一個返回Task<Item[]>findItemsByProductAsync。這樣,您不必使用StartNewTask.Run創建不必要的任務。

然後你的代碼可以是這樣的:

int total = result.paginationOutput.totalPages; 

// Start all downloads; each download is represented by a task. 
Task<Item[]>[] tasks = Enumerable.Range(2, total - 1) 
    .Select(i => client.findItemsByProductAsync(i)).ToArray(); 

// Wait for all downloads to complete. 
Item[][] results = await Task.WhenAll(tasks); 

// Flatten the results into a single collection. 
return results.SelectMany(x => x).ToArray(); 
0

如果你想使用異步等待正常,你必須聲明你的函數異步,並打電話給你的功能也必須是異步。這會一直持續到您有一次啓動異步過程的同步功能。

你的功能應該是這樣的:

由你沒有描述什麼是在列表中的方式。我以爲他們是類型T.在這種情況下result.SearchResult.Item返回 對象 IEnumerable的

private async Task<List<T>> FindItems(...) 
{ 
    int total = result.paginationOutput.totalPages; 
    var newList = new List<T>(); 
    for (int i = 2; i < total + 1; i++) 
    { 
     IEnumerable<T> result = await Task.Factory.StartNew(() => 
     { 
      return client.findItemsByProduct(i); 
     }); 
     newList.AddRange(result.searchResult.item); 
    } 
    return newList; 
} 

如果你做這種方式,你的函數將是異步的,但findItemsByProduct將執行一個之後。如果你想同時執行它們,你不應該等待結果,而是在完成前一個任務之前開始下一個任務。一旦所有任務都開始等待,直到所有任務完成。像這樣:

private async Task<List<T>> FindItems(...) 
{ 
    int total = result.paginationOutput.totalPages; 
    var tasks= new List<Task<IEnumerable<T>>>(); 

    // start all tasks. don't wait for the result yet 
    for (int i = 2; i < total + 1; i++) 
    { 
     Task<IEnumerable<T>> task = Task.Factory.StartNew(() => 
     { 
      return client.findItemsByProduct(i); 
     }); 
     tasks.Add(task); 
    } 
    // now that all tasks are started, wait until all are finished 
    await Task.WhenAll(tasks); 
    // the result of each task is now in task.Result 
    // the type of result is IEnumerable<T> 
    // put all into one big list using some linq: 
    return tasks.SelectMany (task => task.Result.SearchResult.Item) 
     .ToList(); 
    // if you're not familiar to linq yet, use a foreach: 
    var newList = new List<T>(); 
    foreach (var task in tasks) 
    { 
     newList.AddRange(task.Result.searchResult.item); 
    } 
    return newList; 
}