线程安全缓冲区

时间:2018-02-09 18:07:06

标签: c# .net multithreading event-handling

我正在实施某种缓冲机制:

background:rosybrown

问题是 - 我是否应该费心添加某种锁,以确保没有数据丢失(其他一些线程会在buffer.ToList()之后和buffer.Clear()之前添加产品,假设:),或 ConcurrentQueue 会为我处理所有肮脏的工作吗?

2 个答案:

答案 0 :(得分:2)

你可以这样做:

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

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

你永远不会Clear队列。你只是把它拿出去,直到它空了。或者你可以在productsToSave达到一定规模时停止处理,处理该列表,如果你不想一次保存太多产品,就可以开始一个新的。

这样,将新项目添加到队列中并不重要。如果他们在您从队列中读取时已添加,则他们也会被阅读。如果在您停止从队列中读取后立即添加它们,那么它们就会在那里并在下次队列变满并且您处理它时进行读取。

ConcurrentQueue的要点是你可以添加它并从多个线程中读取它,而不需要lock

如果你这样做:

    productsToSave = ProductBuffer.ToList();

    ProductBuffer.Clear();

然后你需要lock(这会破坏目的。)大概是你使用ConcurrentQueue,因为多个线程可能正在向队列中添加项目。如果是这种情况那么完全有可能某些东西可以在执行这两个语句之间进入队列。它不会被添加到列表中,但会被Clear删除。该项目将丢失。

答案 1 :(得分:1)

这是我实现它的方式,我假设您不需要在保存完成时收到通知?

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);
}

如果你得到1件产品,并且没有得到任何其他产品,你会不想在超时后保存这件产品怎么办?

我会为您的用例使用Rx之类的内容,尤其是IObservable<T>.Buffer(count)