Mr Heelis
Mr Heelis

Reputation: 2546

BlockingCollection that doesn't try again within 10 seconds

I am using a Blockingcollection as a FIFO queue but I am doing a lot of operations on files, where the consumer may easily encounter a file lock, so what I have done is created a simple try catch where the consumer re-queue's itself, but in a long FIFO queue with lots of other Items in the queue this is enough of a pause, but in an empty or very short FIFO queue it means the consumer perpetually hammers the queue with repeating re-occurrences of itself that are probably going to be still file locked.

i.e.

consumer busy -> requeue -> consumer busy -> requeue (ad infinitum)

is there a way to get the BlockingCollection to not attempt to run the new consumer if it is less than 10 seconds old? i.e. potentially get the net one in the queue and carry on and only take the next consumer if it's createdDateTime is null (default for first attempt) or if it is > 10 seconds?

Upvotes: 2

Views: 460

Answers (2)

xanatos
xanatos

Reputation: 111870

You could keep two blocking collections: the main one and the "delayed" one. One worker thread would only work on the delayed one, readding them to the main collection. The signature of the rejected collection would be something like:

BlockingCollection<Tuple<DateTime, YourObject>>

now... If the time is fixed at 10 seconds, the delayed collection will nearly be DateTime sorted (in case of items added nearly at the same time this could be not-true, but we are speaking of milliseconds difference... not a problem)

public class MainClass
{
    // The "main" BlockingCollection
    // (the one you are already using)
    BlockingCollection<Work> Works = new BlockingCollection<Work>();

    // The "delayed" BlockingCollection
    BlockingCollection<Tuple<DateTime, Work>> Delayed = new BlockingCollection<Tuple<DateTime, Work>>();

    // This is a single worker that will work on the Delayed collection
    // in a separate thread
    public void DelayedWorker()
    {
        Tuple<DateTime, Work> tuple;

        while (Delayed.TryTake(out tuple, -1))
        {
            var dt = DateTime.Now;

            if (tuple.Item1 > dt)
            {
                Thread.Sleep(tuple.Item1 - dt);
            }

            Works.Add(tuple.Item2);
        }
    }
}

Upvotes: 2

usr
usr

Reputation: 171178

There's nothing built-in to help with that. Store with each work item the DateTime when it was last attempted (could be null if this is the first attempt). Then, in your processing function wait for TimeSpan.FromSeconds(10) - (DateTime.UtcNow - lastAttemptDateTime) seconds before making the next attempt.

Consider switching to a priority queue that stores items in the order of earliest next attempt datetime.

Upvotes: 3

Related Questions