使用 index in for like 参数生成 List<Task>

Generate List<Task> with using index in for like parameter

我想生成 List<Task> 然后并行调用它。我尝试这样做,但不明白如何在 lambda 表达式中传递索引。在我看来,总是发送 for 的最后一个索引。但是我想在并行计算中使用所有索引...

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

var  valueSize = Values.Count;
for (int i = 1; i <= valueSize; i++)
{
   tasks.Add(Task.Factory.StartNew(
   () => {
      this.Values[i] = this._tcpRepository.GetValueAsync(i).ToString().GetInt32();
   }));
}
Task.WaitAll(tasks.ToArray());

附加信息:

public Dictionary<int, int> Values { get; set; }

在 TcpRepository 中

public async Task<string> GetValueAsync(int digit)
{
   var netStream = this.TcpClient.GetStream();
   if (netStream.CanWrite)
   {
      Byte[] sendBytes = Encoding.UTF8.GetBytes(digit.ToString());
      await netStream.WriteAsync(sendBytes, 0, sendBytes.Length);
   }
   if (netStream.CanRead)
   {
      byte[] bytes = new byte[this.Client.ReceiveBufferSize];

      await netStream.ReadAsync(bytes, 0, (int)this.Client.ReceiveBufferSize);
      return bytes.ToString();
   }
   return null;
}

GetInt32() 这是我对字符串 public static int GetInt32(this string value) 的自定义扩展。字符串可以作为不是数字的字符出现垃圾。

这里有一些问题。

要直接回答您的问题,您需要在 i 上创建一个闭包,以防止在您的异步代码中访问它之前对其进行更新。

您可以通过将 for 循环替换为 Enumerable.Range 来实现。

另一个问题是您 运行 GetValueAsync 在 ThreadPool 上但没有等待它。随后,您的 Task.WaitAll 将仅等待外部 Task,而不是 GetValueAsync.

返回的 Task

这是您可以执行的操作的示例:

var tasks = Enumerable.Range(0, valueSize)
    .Select(async i =>
    {
        string val = await _tcpRepository.GetValueAsync(i);
        Values[i] = val.GetInt32();
    });

你的最后一个问题是Task.WaitAll的使用;这引入了 sync-over-async 反模式。您应该允许异步通过您的代码库增长,而不是使用 Task.WhenAll,其中 returns 一个 Task 在所有提供的 Task 完成时完成:

await Task.WhenAll(tasks);