我有一个ConcurrentStack,我正在将项目转储到其中。在堆栈不为空时一次处理这些项目的一种好方法是什么?我想以一种在不处理堆栈时不会占用CPU周期的方式来执行此操作。

我目前所能获得的基本上是这个,它似乎不是理想的解决方案。

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

因此,bool存在,因此不会备份多个线程,而不会等待锁。但是我不希望有很多事情可以同时处理堆栈,因此不需要锁。因此,如果两个单独的线程最终同时调用了HandleStack并越过了 bool 值,那么锁就在那里,因此它们都不会立即遍历堆栈。但是,一旦第二个通过锁,堆栈将是空的,不会执行任何操作。因此,这最终给了我想要的行为。

因此,实际上我只是在ConcurrentStack周围编写一个伪并发包装器,似乎必须有一种不同的方法来实现这一目标。有什么想法吗?

最佳答案

ConcurrentStack<T>是实现IProducerConsumerCollection<T>的集合之一,因此可以由BlockingCollection<T>包装。 BlockingCollection<T>具有一些方便的成员,可用于常见操作,例如“在堆栈不为空时消费”。例如,您可以循环调用TryTake。或者,您可以只使用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);
}

09-06 03:14