Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Process a ConcurrentStack when not empty?

I've got a ConcurrentStack that I'm dumping items into. What's a good way to process those items one at a time when the stack isn't empty? I'd like to do this in a way that isn't eating up CPU cycles when the stack isn't being processed.

What I've currently got is basically this and it doesn't seem like an ideal solution.

private void AddToStack(MyObj obj)
{
    stack.Push(obj);
    HandleStack();
}

private void HandleStack()
{
    if (handling)
        return;

    Task.Run( () =>
    {
        lock (lockObj)
        {
            handling = true;
            if (stack.Any())
            {
                //handle whatever is on top of the stack
            }
            handling = false;
        }
    }
}

So the bool is there so multiple threads don't get backed up waiting on the lock. But I don't want multiple things handling the stack at once hence the lock. So if two separate threads do end up calling HandleStack simultaneously and get past the bool, the lock is there so both aren't iterating through the stack at once. But once the second gets through the lock the stack'll be empty and doesn't do anything. So this does end up giving me the behavior I want.

So really I'm just writing a pseudo concurrent wrapper around the ConcurrentStack and it seems like there's got to be a different way to achieve this. Thoughts?

like image 503
claudekennilol Avatar asked Sep 23 '26 19:09

claudekennilol


1 Answers

ConcurrentStack<T> is one of the collections that implements IProducerConsumerCollection<T>, and as such can be wrapped by BlockingCollection<T>. BlockingCollection<T> has several convenience members for common operations like "consume while the stack is not empty". E.g., you could call TryTake in a loop. Or, you could just use GetConsumingEnumerable:

private BlockingCollection<MyObj> stack;
private Task consumer;

Constructor()
{
  stack = new BlockingCollection<MyObj>(new ConcurrentStack<MyObj>());
  consumer = Task.Run(() =>
  {
    foreach (var myObj in stack.GetConsumingEnumerable())
    {
      ...
    }
  });
}

private void AddToStack(MyObj obj)
{
  stack.Add(obj);
}
like image 118
Stephen Cleary Avatar answered Sep 25 '26 10:09

Stephen Cleary



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!