C# 如何安全地迭代IAsyncEnumerable以向下游发送集合,以便成批处理消息

C# 如何安全地迭代IAsyncEnumerable以向下游发送集合,以便成批处理消息,c#,.net,collections,iterator,iasyncenumerable,C#,.net,Collections,Iterator,Iasyncenumerable,我已经观看了聊天,它让我对如何处理iSyncEnumerable的扩展方法有了一些见解,但对于真实的应用程序,尤其是对于我的经验水平,还不够详细,而且我了解到,iSyncEnumerable的示例/文档目前还不存在 我试图从一个文件中读取,对流进行一些转换,返回一个IAsyncEnumerable,然后在获得任意数量的对象后将这些对象发送到下游,如: await foreach (var data in ProcessBlob(downloadedFile)) { //todo add

我已经观看了聊天,它让我对如何处理iSyncEnumerable的扩展方法有了一些见解,但对于真实的应用程序,尤其是对于我的经验水平,还不够详细,而且我了解到,
iSyncEnumerable
的示例/文档目前还不存在

我试图从一个文件中读取,对流进行一些转换,返回一个
IAsyncEnumerable
,然后在获得任意数量的对象后将这些对象发送到下游,如:

await foreach (var data in ProcessBlob(downloadedFile))
{
    //todo add data to List<T> called listWithPreConfiguredNumberOfElements
    if (listWithPreConfiguredNumberOfElements.Count == preConfiguredNumber)
        await _messageHandler.Handle(listWithPreConfiguredNumberOfElements);
        
    //repeat the behaviour till all the elements in the IAsyncEnumerable returned by ProcessBlob are sent downstream to the _messageHandler.
}
wait foreach(ProcessBlob(下载文件)中的var数据)
{
//将数据添加到名为listWithPreConfiguredNumberOfElements的列表中的步骤
if(listWithPreConfiguredNumberOfElements.Count==预配置编号)
wait_messageHandler.Handle(带有预先配置的numberofelements的列表);
//重复该行为,直到ProcessBlob返回的IAsyncEnumerable中的所有元素都被发送到下游的_messageHandler。
}
到目前为止,我从阅读中了解到,
await foreach
行正在处理使用
Task
s(或
ValueTask
s)的数据,因此我们没有预先计数。我还不太确定是否使用列表变量,而只是对其进行长度检查,因为跨线程共享数据似乎不是非常线程安全的


我正在使用
System.Linq.Async
包,希望能够使用相关的扩展方法。我可以看到一些关于
TakeWhile
的承诺,但我对我打算执行的任务的线程安全性的理解并不完全存在,这使我失去了信心


非常感谢您提供的任何帮助或推动,谢谢。

软件包中有一个操作员
缓冲区
,可以满足您的需要

//将异步可枚举序列的每个元素投影到连续的
//基于元素计数信息生成的非重叠缓冲区。
公共静态IAsyncEnumerable缓冲区(
此IAsyncEnumerable源,int count);
此包包含诸如
Amb
Throw
Catch
Defer
Finally
等运算符,它们在Linq中没有直接等效项,但在Linq中确实有等效项。这是因为
IAsyncEnumerable
s在概念上更接近
IObservable
s,而不是
IEnumerable
s(因为两者都有时间维度,而
IEnumerable
s是永恒的)

我还不太确定是否使用列表变量,而只是对其进行长度检查,因为跨线程共享数据似乎不是非常线程安全的

在处理
async
时,您需要考虑执行流,而不是线程;由于您正在等待处理步骤,因此访问列表实际上并不存在并发问题,因为无论使用哪个线程:列表一次只访问一次

如果您仍然担心,您可以
new
每批列出一个列表,但这可能有点过头了。然而,您需要的是两个添加—批次之间的重置和最终处理步骤:

var listWithPreConfiguredNumberOfElements=新列表(preConfiguredNumber);
等待foreach(ProcessBlob(下载文件)中的var数据)//CAF?
{
带有预先配置的元素数的列表。添加(数据);
if(listWithPreConfiguredNumberOfElements.Count==预配置编号)
{
wait _messageHandler.Handle(listWithPreConfiguredNumberOfElements);//CAF?
listWithPreConfiguredNumberOfElements.Clear();//为新批次重置
//(如果您仍然担心并发性,请将其替换为“新的”)
}
}
if(列表带有预配置的numberofelements.Any())
{//处理任何掉队者
wait _messageHandler.Handle(listWithPreConfiguredNumberOfElements);//CAF?
}

您也可以选择在标有
//CAF?

系统的三个点中使用
ConfigureAwait(false)
。Linq.Async
是反应式扩展的一部分。我的第一个想法是TPL数据流,带有…示例和文档。人们认为IAsyncEnumerable比实际情况要多。这只是异步枚举的一种方式,不是构造管道的新方式,也不是多线程的新方式。它既不是数据流块,也不是通道。它可以是管道中各个步骤之间的粘合剂。您是否可以使用列表或需要ConcurrentQueue取决于处理代码的工作方式,而不是源代码(IAsyncEnumerable),正如它不依赖于
IEnumerable
。如果有多个任务从源消耗,则需要
ConcurrentQueue
。如果您只有一个任务,则可以使用
列表
,但这将阻止您使用多个任务。批处理操作不需要多个任务,尽管我认为您应该首先清理代码,将其转换为一种便于创建管道的形式。字段级处理程序使事情变得更加困难。使用LINQ风格的方法要容易得多,这些方法接受
IAsyncEnumerable
作为参数并返回另一个参数。您可以一个接一个地链接多个方法来创建管道,始终知道每个方法的作用、如何处理并发性等。方法
IAsyncEnumerable
Batch(此IAsyncEnumerable源,int batchSize)`允许
ProcessBlob(downloadedFile)。Batch(100)..
正是思维的转变为我解决了这一问题——某种程度上是范式的转变,并造成了混乱。我对此投了赞成票,因为解决方案是显而易见的,但它背后的解释对我来说太过分了,谢谢
// Projects each element of an async-enumerable sequence into consecutive
// non-overlapping buffers which are produced based on element count information.
public static IAsyncEnumerable<IList<TSource>> Buffer<TSource>(
    this IAsyncEnumerable<TSource> source, int count);