2013-06-25 54 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; 
}