【问题标题】:How to add index to async LINQ query如何将索引添加到异步 LINQ 查询
【发布时间】:2017-03-15 02:20:24
【问题描述】:

使用不带 async 关键字的 lambda 表达式的 LINQ 表达式可以指定索引。对于example

var list = FileList.Select((file, index) => new { Index=index, Filename=file });

我试图在使用 async 关键字时获取索引。例如:

await Task.WhenAll(urlList.Select(async url =>
{
   byte[] urlContents = await GetWebPageAsync(url);
   lock (Locker) { webResults.Add(URLContents); }
}));

我想获取索引的原因是为了可以将网页内容存储在数组中,而不是使用锁定语句或在集合中搜索索引。

当我尝试为上述查询添加索引时,它会给出编译器错误。

有没有办法在上述查询中指定索引,或者是否可以使用另一个支持使用带索引的异步 lambda 表达式的 LINQ 表达式(Select 除外)?

【问题讨论】:

  • 你不应该在你的预测中产生副作用。这在普通代码中已经够糟糕了,但是在处理多线程代码时,它就更难处理了。执行投影的 lambda 应该返回操作的结果,而不是将它们添加到列表中。然后,您可以使用返回序列的结果。

标签: c# linq lambda async-await


【解决方案1】:

我刚刚意识到,代码从等待返回时始终是单线程的 - 并且始终位于调用它的同一线程上。在这种情况下,它是一个用户界面线程。因此,不需要 lock 语句。

为了验证这一点,我进行了一些调用以获取线程 ID:

string ThreadID = Thread.CurrentThread.ManagedThreadId.ToString();
await Task.WhenAll(urlList.Select(async url =>
{
   string TaskThreadID = Thread.CurrentThread.ManagedThreadId.ToString();
   byte[] URLContents = await GetWebPageAsync(url);
   string TaskThreadID2 = Thread.CurrentThread.ManagedThreadId.ToString();
   lock (locker) { webResults.Add(URLContents); }
     }));

它们都是相同的值。我对 Task.WhenAll 方法的工作原理有误解。

尽管在这种情况下我不需要索引,但有时它会有所帮助。如果有人能说明如何优雅地获取索引,我会接受答案。

【讨论】:

  • 为了优雅的解决方案,只需使用两个select语句FileList.Select((file, index) => new { Index=index, Filename=file }).Select(async data => { foo = data.Index; \\... });
  • @ScottChamberlain 我会试试看的。
  • @ScottChamberlain 看起来它会起作用,但是代码会为集合中的每个元素创建一个新对象。我不知道这是否比对集合中的每个 url 进行顺序搜索(当集合很小时)以查找索引或在索引较大时使用字典更好。 Microsoft 应该添加一个重载方法,该方法可以与 async 一起使用以获取集合中的索引 - 就像它使用常规 LINQ 一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-25
  • 2010-11-12
相关资源
最近更新 更多