C# 批处理程序(从队列中聚合项目)

C# 批处理程序(从队列中聚合项目),c#,multithreading,collections,locking,C#,Multithreading,Collections,Locking,我有一个System.Timers.Timer每3秒运行一次。 一旦它过去了,我想把我的收藏中的所有项目,并处理他们在一批 这样做的动机是减少后端系统上的I/O数量 挑战在于我有多个并发线程附加到集合/队列。因此,我考虑使用ConcurrentQueue——但这是一个糟糕的选择 这很好地描述了这里的问题 我需要的是一个集合/队列,在这里我可以一次获取所有数据(ToArray()),并在一个原子操作中清除队列,这样我就不会丢失同时被其他线程写入集合/队列的任何数据。 private static

我有一个
System.Timers.Timer
每3秒运行一次。
一旦它过去了,我想把我的收藏中的所有项目,并处理他们在一批

这样做的动机是减少后端系统上的I/O数量

挑战在于我有多个并发线程附加到集合/队列。因此,我考虑使用
ConcurrentQueue
——但这是一个糟糕的选择

这很好地描述了这里的问题

我需要的是一个集合/队列,在这里我可以一次获取所有数据(ToArray()),并在一个原子操作中清除队列,这样我就不会丢失同时被其他线程写入集合/队列的任何数据。

 private static void T1_Elapsed(object sender, ElapsedEventArgs e)
 {
    string[] result = _queue.ToArray();
   _queue = new ConcurrentQueue<string>(); // strings will be lost :-)
 }
现在,这段代码有一个明显的缺陷,可以在生产者代码中看到:

private static void ProduceItems()
{
    //while (!_stop)
    for(int i=0; i<int.MaxValue; i++)
    {
        if (_stop) break;

        lock (_myLock) // bad. locks out other producers running on other threads.
        {
            Console.WriteLine("Enqueue " + i);
            _queue.Enqueue("string" + i);
        }

        Thread.Sleep(1000); // FOR DEBUGGING PURPOSES ONLY
    }
}
private static void ProduceItems()
{
//而(!\u停止)

对于(int i=0;iTPL数据流

我要说的是尝试一下TPL数据流库。它是基于任务并行库构建的,专为并发性扮演重要角色的此类需求而设计。有关此库的一系列博客文章,请参阅

BatchBlock
似乎非常适合您的场景。有关教程,请参阅

使用批处理块的另一个示例是:

您将发布到一个可用的TPL数据流块,而不是将数据发布到队列

另一个选择是使用

反应式扩展

请参阅以获得良好的介绍

它还提供批处理支持:

void Sample()
{
    var dataprovider = new Subject<int>();

    var subscription = dataprovider
        .Buffer(TimeSpan.FromMinutes(3))
        .Subscribe(listOfNumbers => 
        {
            // do something with batch of items
            var batchSize = listOfNumbers.Count;
        });

    for(int i = 0; i <= 5; ++i)
    {
        dataprovider.OnNext(i);
    }

    subscription.Dispose();
}
void Sample()
{
var dataprovider=新主题();
var subscription=dataprovider
.缓冲区(时间跨度从分钟(3))
.订阅(列表编号=>
{
//对一批物品做些什么
var batchSize=listOfNumbers.Count;
});

对于(int i=0;我知道外面有东西;-)RX扩展给我留下了深刻的印象。你的例子真的很有帮助。现在我只需要弄清楚任何重试/失败机制是如何融入其中的。你可以将它们发送到另一个RX流,并在特定的订阅操作中处理它们。也有内置的错误处理策略,但这取决于你需要什么样的操作万一失败了。
internal class Rx
{
    internal static void Start()
    {
        ISubject<int> subject = new Subject<int>();
        ISubject<int> syncedSubject = Subject.Synchronize(subject); // that should do it? - UNTESTED!

        var subscription = syncedSubject.Buffer(TimeSpan.FromSeconds(5), 10)
            .Subscribe((item) => ProcessBatch(item));

        for (int i=1; i<int.MaxValue; i++)
        {
            syncedSubject.OnNext(i);
            Thread.Sleep(200);
            Console.WriteLine($"Produced {i}.");
        }

        Console.ReadKey();
        subscription.Dispose();
    }

    private static void ProcessBatch(IList<int> list)
    {
        // Aggregate many into one
        string joined = string.Join(" ", list);

        // Process one
        Console.WriteLine($"Wrote {joined} to remote storage.");

        // how do you account for errors here?
        myProducer.ReEnqueueMyFailedItems(list); // ?
    }
}
void Sample()
{
    var dataprovider = new Subject<int>();

    var subscription = dataprovider
        .Buffer(TimeSpan.FromMinutes(3))
        .Subscribe(listOfNumbers => 
        {
            // do something with batch of items
            var batchSize = listOfNumbers.Count;
        });

    for(int i = 0; i <= 5; ++i)
    {
        dataprovider.OnNext(i);
    }

    subscription.Dispose();
}