简体   繁体   English

线程安全缓冲区

[英]Thread-safe buffer

I'm implementing some kind of buffering mechanism: 我正在实现某种缓冲机制:

private static readonly ConcurrentQueue<ProductDto> ProductBuffer = new ConcurrentQueue<ProductDto>();

private async void On_ProductReceived(object sender, ProductReceivedArgs e)
{
    ProductBuffer.Enqueue(e.Product);

    if (ProductBuffer.Count >= handlerConfig.ProductBufferSize)
    {
        var products = ProductBuffer.ToList();
        ProductBuffer.Clear();

        await SaveProducts(products);
    }
}

And the question is - should I bother to add some kind of lock, to ensure no data is lost (fe some other thread will add product after buffer.ToList() and before buffer.Clear(), hypothetically:), or ConcurrentQueue will handle all the dirty work for me? 问题是-我是否应该麻烦地添加某种锁,以确保没有数据丢失(假设某些其他线程会在buffer.ToList()之后和buffer.Clear()之前添加乘积,假设是:),或ConcurrentQueue将为我处理所有肮脏的工作?

You can do it like this: 您可以这样做:

if (ProductBuffer.Count < handlerConfig.ProductBufferSize)
    return;

var productsToSave = new List<Product>();
Product dequeued = null;
while(ProductBuffer.TryDequeue(out dequeued))
{
    productsToSave.Add(dequeued);
}
SaveProducts(products);

You never Clear the queue. 您永远不会Clear队列。 You just keep taking things out until it's empty. 您只要不断取出东西,直到它变空为止。 Or you could stop taking things out when productsToSave reaches a certain size, process that list, and then start a new one if you don't want to save too many products at once. 如果您不想一次保存太多产品,或者当productsToSave达到一定大小时可以停止处理,处理该列表,然后开始新的列表。

This way it doesn't matter if new items are added to the queue. 这样,是否将新项目添加到队列都没有关系。 If they're added while you're reading from the queue, they get read too. 如果在您从队列中读取时添加了它们,它们也会被读取。 If they're added just after you stop reading from the queue, they'll be there and get read the next time the queue gets full and you process it. 如果是在您停止从队列中读取后才添加的,则它们将在那里并在队列下一次充满并处理时读取。

The point of a ConcurrentQueue is that you can add to it and read from it from multiple threads, with no need for lock . ConcurrentQueue的要点是,您可以添加到其中并从多个线程中读取它,而无需lock


If you were to do this: 如果要这样做:

    productsToSave = ProductBuffer.ToList();

    ProductBuffer.Clear();

then you would need the lock (which would defeat the purpose.) Presumably you're using a ConcurrentQueue because multiple threads may be adding items to the queue. 那么您将需要该lock (这将无法达到目的。)大概您正在使用ConcurrentQueue因为可能有多个线程将项目添加到队列中。 If that's the case then it is entirely possible that something could go into the queue in between the execution of those two statements. 如果真是这样,那么在这两个语句的执行之间完全有可能进入队列。 It wouldn't get added to the list, but it would be deleted by Clear . 它不会添加到列表中,但会被Clear删除。 That item would be lost. 该项目将丢失。

This is how I would implement it, I am assuming you do not need to be notified of when the save is finished? 这是我要实现的方式,我假设不需要在保存完成时通知您?

private void On_ProductReceived(object sender, ProductReceivedArgs e)
{
    // Variable to hold potential list of products to save
    List<Products> productsToSave;

    // Lock buffer
    lock(ProductBuffer)
    {
        ProductBuffer.Enqueue(e.Product);

        // If it is under size, return immediately
        if (ProductBuffer.Count < handlerConfig.ProductBufferSize)
            return;

        // Otherwise save products, clear buffer, release lock.
        productsToSave = ProductBuffer.ToList();

        ProductBuffer.Clear();
    }

    // Save Produts, 

    SaveProducts(products);
}

What if you get 1 product, and don't get anything else, will you not want to save this after some timeout? 如果您只获得1种产品,而又什么也没有得到,怎么办?您是否不想在超时后保存该产品?

I would use something like Rx for your use case, especially IObservable<T>.Buffer(count) 我会在您的用例中使用Rx东西,尤其是IObservable<T>.Buffer(count)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM