簡體   English   中英

阻止舊數據的BlockingCollection

[英]BlockingCollection that discards old data

我有一個BlockingCollection 生產者任務向其添加項目,消費者任務刪除項目。

現在我想限制集合中的項目數,如果添加更多項目,則自動丟棄舊數據。 該集合永遠不應包含多於N最近添加的項目。

因此,如果生產者添加新項目的速度快於消費者刪除它們,我希望消費者只處理最新的項目。

我可以在其構造函數中限制BlockingCollection的大小,但當然這只是意味着它在添加更多項時會阻塞,而不是它會刪除舊項。

(我不希望生產者端阻塞,只有消費者方在從空集合中檢索項目時才會阻塞。)

我目前的解決方案是黑客,只適用於1的大小限制:
(而且我不太確定它是否可靠。)

// My consumer task:
foreach (var item in blockingCollection.GetConsumingEnumerable())
{
    var lastItem = item;
    var lastItemTmp = item;
    while (blockingCollection.TryTake(out lastItemTmp))
           lastItem = lastItemTmp;
    // Now lastItem contains the most recent item in the collection, 
    // and older items have been discarded.
    // Proceed consuming lastItem ...
}

有更清潔的解決方案嗎?

這樣做:

void AddItemToQueue(MyClass item)
{
    while (!queue.TryAdd(item))
    {
        MyClass trash;
        queue.TryTake(out trash);
    }
}

如果在嘗試添加項目時隊列已滿,則會從隊列中刪除項目。 它使用TryTake因為有可能(不太可能,但可能)某個其他線程可能已經從隊列中刪除了最后一個項目,然后才有機會獲取一個。

當然,這假設您在構造BlockingCollection時指定了項目數限制。

另一種方法是,盡管它涉及的更多,但是要創建自己的循環隊列類,並讓它實現IProducerConsumerCollection接口。 然后,您可以使用該類的實例作為BlockingCollection的后備集合。 實現循環隊列並不是特別困難,盡管邊緣情況很難實現。 而且你必須使它成為一個並發的數據結構,盡管用鎖很容易。

如果您不希望隊列經常溢出,或者隊列的流量非常低(即每秒沒有被擊中數千次),那么我的初步建議將做您想做的事情並且不存在性能問題。 如果存在性能問題,則循環隊列就是解決方案。

我會使用Concurrent堆棧:

表示線程安全的后進先出(LIFO)集合。

http://msdn.microsoft.com/en-us/library/dd267331%28v=vs.110%29.aspx

我會在堆棧中發送一個包裝你的任務的對象,為它添加一個時間戳。 使用者將從堆棧中獲取任務並丟棄時間戳超過您定義的閾值的任務。

只需在向其中添加項目之前調用此方法即可。

public static void Clear<T>(this BlockingCollection<T> blockingCollection)
    {
        if (blockingCollection == null)
        {
            throw new ArgumentNullException("blockingCollection");
        }

        while (blockingCollection.Count > 0)
        {
            T item;
            blockingCollection.TryTake(out item);
        }
    }

暫無
暫無

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

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