C# &引用;“无限”;异步并行foreach循环

C# &引用;“无限”;异步并行foreach循环,c#,asynchronous,parallel-processing,async-await,C#,Asynchronous,Parallel Processing,Async Await,我有一个包含5万到10万个单词的列表 我想以并行和异步的方式对其进行迭代 例如,我可以使用 while (true) { Parallel.ForEach(words, new ParallelOptions { MaxDegreeOfParallelism = 100 }, ...) } 但问题是: Parallel.ForEach不是异步的 当我们到达列表的末尾时,我们必须等待每个线程在while(true)语句继续之前结束 这意味着不总是有100个线程在运行,这就是我想要的 我怎样

我有一个包含5万到10万个单词的
列表

我想以并行和异步的方式对其进行迭代

例如,我可以使用

while (true)
{
   Parallel.ForEach(words, new ParallelOptions { MaxDegreeOfParallelism = 100 }, ...)
}
但问题是:

  • Parallel.ForEach
    不是异步的
  • 当我们到达列表的末尾时,我们必须等待每个线程在
    while(true)
    语句继续之前结束
  • 这意味着不总是有100个线程在运行,这就是我想要的
  • 我怎样才能做到这一点


    请让我知道这是否令人困惑,或者我是否不善于解释问题。

    下面是一个完全人为的
    异步
    友好示例,说明如何实现您的要求

  • 它适用于异步IO工作负载
  • 可以取消
  • 它限制了最大并行性
  • 它的容量有限,因此总有100个工作岗位可用
  • 它是无限的
  • 给定的

    private static CancellationTokenSource _cs;
    private static CancellationToken _token;
    private static ActionBlock<string> _block;
    
    private static async Task MethodAsync(string something)
    {
       // Your async workload
    }
    
    public static async Task EndlessRunner(string[] someArray)
    {
       try
       {
          var index = 0;
          while (!_token.IsCancellationRequested)
          {
             await _block.SendAsync(someArray[index],_token);
             if (++index >= someArray.Length) index = 0;
          }
       }
       catch (OperationCanceledException)
       {
          Console.WriteLine("Cancelled");
       }
    }
    
    private static CancellationTokenSource\u cs;
    私有静态取消令牌_令牌;
    私有静态动作块_块;
    私有静态异步任务方法异步(字符串)
    {
    //您的异步工作负载
    }
    公共静态异步任务EndlessRunner(字符串[]someArray)
    {
    尝试
    {
    var指数=0;
    while(!\u token.iscancellationrequest)
    {
    wait _block.SendAsync(someArray[index],_令牌);
    如果(++index>=someArray.Length)index=0;
    }
    }
    捕获(操作取消异常)
    {
    控制台。写入线(“取消”);
    }
    }
    
    示例

    private static async Task Main()
    {
       _cs = new CancellationTokenSource();
       _token = _cs.Token;
    
       _block = new ActionBlock<string>(
          MethodAsync, 
          new ExecutionDataflowBlockOptions()
          {
             EnsureOrdered = false,
             MaxDegreeOfParallelism = 100,
             BoundedCapacity = 100,
             CancellationToken = _cs.Token,
             SingleProducerConstrained = true
          });
    
       var someList = Enumerable
          .Range(0,5000)
          .Select(I => $"something {I}")
          .ToArray();
    
       Task.Run(() => EndlessRunner(someList));
    
       Console.ReadKey();
    
       _cs.Cancel();
    
       _block.Complete();
       await _block.Completion;
    
    }
    
    private静态异步任务Main()
    {
    _cs=新的CancellationTokenSource();
    _令牌=_cs.token;
    _块=新动作块(
    MethodAsync,
    新的ExecutionDataflowBlockOptions()
    {
    重新排序=错误,
    MaxDegreeOfParallelism=100,
    边界容量=100,
    CancellationToken=\u cs.Token,
    SingleProducerConstrained=true
    });
    var someList=可枚举
    .范围(05000)
    .Select(I=>$“something{I}”)
    .ToArray();
    Task.Run(()=>EndlessRunner(someList));
    Console.ReadKey();
    _cs.Cancel();
    _block.Complete();
    等待区块完工;
    }
    
    这是一个完全人为设计的
    异步
    友好示例,说明如何实现您的要求

  • 它适用于异步IO工作负载
  • 可以取消
  • 它限制了最大并行性
  • 它的容量有限,因此总有100个工作岗位可用
  • 它是无限的
  • 给定的

    private static CancellationTokenSource _cs;
    private static CancellationToken _token;
    private static ActionBlock<string> _block;
    
    private static async Task MethodAsync(string something)
    {
       // Your async workload
    }
    
    public static async Task EndlessRunner(string[] someArray)
    {
       try
       {
          var index = 0;
          while (!_token.IsCancellationRequested)
          {
             await _block.SendAsync(someArray[index],_token);
             if (++index >= someArray.Length) index = 0;
          }
       }
       catch (OperationCanceledException)
       {
          Console.WriteLine("Cancelled");
       }
    }
    
    private static CancellationTokenSource\u cs;
    私有静态取消令牌_令牌;
    私有静态动作块_块;
    私有静态异步任务方法异步(字符串)
    {
    //您的异步工作负载
    }
    公共静态异步任务EndlessRunner(字符串[]someArray)
    {
    尝试
    {
    var指数=0;
    while(!\u token.iscancellationrequest)
    {
    wait _block.SendAsync(someArray[index],_令牌);
    如果(++index>=someArray.Length)index=0;
    }
    }
    捕获(操作取消异常)
    {
    控制台。写入线(“取消”);
    }
    }
    
    示例

    private static async Task Main()
    {
       _cs = new CancellationTokenSource();
       _token = _cs.Token;
    
       _block = new ActionBlock<string>(
          MethodAsync, 
          new ExecutionDataflowBlockOptions()
          {
             EnsureOrdered = false,
             MaxDegreeOfParallelism = 100,
             BoundedCapacity = 100,
             CancellationToken = _cs.Token,
             SingleProducerConstrained = true
          });
    
       var someList = Enumerable
          .Range(0,5000)
          .Select(I => $"something {I}")
          .ToArray();
    
       Task.Run(() => EndlessRunner(someList));
    
       Console.ReadKey();
    
       _cs.Cancel();
    
       _block.Complete();
       await _block.Completion;
    
    }
    
    private静态异步任务Main()
    {
    _cs=新的CancellationTokenSource();
    _令牌=_cs.token;
    _块=新动作块(
    MethodAsync,
    新的ExecutionDataflowBlockOptions()
    {
    重新排序=错误,
    MaxDegreeOfParallelism=100,
    边界容量=100,
    CancellationToken=\u cs.Token,
    SingleProducerConstrained=true
    });
    var someList=可枚举
    .范围(05000)
    .Select(I=>$“something{I}”)
    .ToArray();
    Task.Run(()=>EndlessRunner(someList));
    Console.ReadKey();
    _cs.Cancel();
    _block.Complete();
    等待区块完工;
    }
    
    您几乎肯定不希望100个线程始终运行……1)为什么希望它是异步的?考虑到它在一个无限循环中,这将是危险的。另外,您不应该将
    async/await
    Parallel.xxx
    2)一起使用,因此3)可能是,可能不是
    Parallel.xxx
    的动态线程池大小调整非常智能。您可能需要考虑TPL数据流,因为它支持流水线;限制和限制,并与async配合使用。TPLDF同时满足CPU绑定和I/O绑定的作业。如果您确实希望同时运行100个线程(同样,您确实不希望,您一定很困惑),那么您必须手动使用线程对象。Net中的异步实现将自行处理线程以获得最佳数量,并且除非您有128个核心服务器,否则它不会分配100个线程一次运行。任何时候使用wait时,线程池线程都会选择延续,并且很可能不会有100个线程池线程“我正在使用HttpClient”-那么您肯定不想增加“100个线程”。旋转一个只等待I/O的线程是对线程的浪费。让一个线程只处理一个网络链接更糟糕
    Parallel.xxx
    使用
    async/await
    非常危险,您希望后者支持IOCP。最好使用TPL数据流和
    async/await
    。对于这类事情来说,这真是太棒了
    Parallel.ForEach
    最适合用于CPU限制的操作,这似乎是一个X/Y问题-为什么您认为需要同时运行100个线程?您几乎肯定不希望总是运行100个线程…1)为什么希望它是异步的?考虑到它在一个无限循环中,这将是危险的。另外,您不应该将
    async/await
    Parallel.xxx
    2)一起使用,因此3)可能是,可能不是
    Parallel.xxx
    的动态线程池大小调整非常智能。您可能需要考虑TPL数据流,因为它支持流水线;限制和限制,并与async配合使用。TPLDF同时满足CPU绑定和