簡體   English   中英

GroupBy 如何與 IAsyncEnumerable 一起使用?

[英]How GroupBy works with IAsyncEnumerable?

我已經在有分頁的HttpClient請求中實現IAsyncEnumerable ,但我還需要對它們進行GroupBy 所以我實現了如下代碼;

public class Item 
{
   public int Id {get; set;}
   public string Name {get; set;}
}

public async IAsyncEnumerable<Item> GetItems()
{
   while(hasMorePage)
   {
       // ... get paginated items

       foreach(var item in paginatedItems)
       {
         yield return item;
       }
   }
}

// should find most repeated item(by Id) and count of them.
public async Task GroupItems()
{
  IAsyncEnumerable<Item> items = GetItems();
  
  //IAsyncGrouping
  await foreach(var item in items.GroupBy(i => i.Id).
                                  OrderByDescendingAwait(i => i.CountAsync()).
                                  Take(10))
 {
    Console.WriteLine(item.Key.ToString() + (await item.CountAsync()).ToString());
 }
}

正如我所料,這段代碼工作得很好。 但我想了解GroupBy是如何在這里工作的,因為它應該有所有按 id 分組的項目有什么我想念的嗎? 或者有什么我可以重構性能的嗎?

首先,評論中鏈接的 ALinq repo 與 .NET 的 IAsyncEnumerable 或 System.Linq.Async 無關。 這是一個 8 年歷史的存儲庫,甚至沒有針對 .NET 核心。 System.Linq.Async 由為 .NET 構建 Reactive Excetions 的同一團隊維護,其代碼位於同一 Github 存儲庫

其次,不清楚需要解釋什么行為。

  • GroupBy會阻止嗎? 不,它沒有。
  • GroupBy是否必須在產生結果之前消耗整個源? 是的,它確實。

如果您有長時間運行的 stream 事件,則必須等到 stream 結束才能獲得任何結果。 這是因為 GroupBy 在其分配階段計算分組,然后在其迭代階段返回它們

protected override async ValueTask<bool> MoveNextCore()
{
    switch (_state)
    {
        case AsyncIteratorState.Allocated:
            _lookup = await Internal.Lookup<TKey, TSource>.CreateAsync(_source, _keySelector, _comparer, _cancellationToken).ConfigureAwait(false);
            _enumerator = _lookup.ApplyResultSelector(_resultSelector).GetEnumerator();
            _state = AsyncIteratorState.Iterating;
            goto case AsyncIteratorState.Iterating;

        case AsyncIteratorState.Iterating:
            if (_enumerator!.MoveNext())
            {
                _current = _enumerator.Current;
                return true;
            }

            await DisposeAsync().ConfigureAwait(false);
            break;
    }

    return false;
}

如果您想處理事件流,您應該查看 Rx.NET,它由創建 System.Linq.Async 的同一團隊構建。 在 Rx.NET 中,當遇到新的鍵值時, GroupBy將發出一個新組 stream:

反應式擴展 GroupBy 插圖

請注意,Rx.NET 的GroupBy實際上按分組鍵對事件 stream 進行分區,並發出而不是分組。 訂閱者將訂閱這些流並處理他們的事件。 聚合示例演示了這一點:

var source = Observable.Interval(TimeSpan.FromSeconds(0.1)).Take(10);
var group = source.GroupBy(i => i % 3);
group.Subscribe(
  grp => 
    grp.Min().Subscribe(
      minValue => 
        Console.WriteLine("{0} min value = {1}", grp.Key, minValue)),
  () => Console.WriteLine("Completed"));

如果您需要處理長時間運行的 IAsyncEnumerable<> stream 您可以使用ToObservable

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM