堆栈空异常

7
我遇到了一个堆栈空异常。如果堆栈不为空(它有16个项目),这怎么可能发生呢?
我截取了错误的快照: Stack Empty Exception 有人能解释一下吗?

2
你已经打上了多线程标签,所以我猜测有许多线程正在访问这段代码。我还猜测SharedMemory不是线程安全的,并且你没有对其进行锁定。我猜得对吗? - Rotem
1
在这个上下文中,“SharedMemory”是什么?“full”又是什么? - Marc Gravell
@leppie,我记得那个API已经因为泛型而被停用了。原因很简单,它是无用的。你不能安全地使用该API,因为它只同步单个操作。你不能检查计数,然后弹出如果> 0,因为这是两个独立的操作,即行为未定义。 - Marc Gravell
共享内存的类型是堆栈,而full是一个事件等待处理器。 - Ibrahim Ahmed
1
@BeyondProgramming 因为 Stack / Stack<T> 没有被列为线程安全的,所以在不同的线程中访问 .Count / .Pop() / .Push() 等内容时没有定义的行为,并且在没有看到执行 Push() 的代码之前,无法评论可能的竞争场景。但最终的解决方案无论如何都是相同的:您需要进行同步,或使用一个线程安全的容器。 - Marc Gravell
显示剩余3条评论
3个回答

7

在使用类似于 Stack<T> 的东西时,您必须同步访问。最简单的方法是使用lock,这也让您可以将lock用于同步本身;因此,弹出操作将如下所示:

int item;
lock (SharedMemory)
{
    while (SharedMemory.Count == 0)
    {
        Monitor.Wait(SharedMemory);
    }
    item = SharedMemory.Pop();
}
Console.WriteLine(item);

并且推送将是:

lock (SharedMemory)
{
    SharedMemory.Push(item);
    Monitor.PulseAll(SharedMemory);
}

按照MBen的建议,使用ConcurrentStack<T>不是更简单吗? - Adam
如果@codesparkle有一个阻塞的“Pop”方法,也许会更好;但它没有。它只有“如果有东西,请给我一些东西”,即TryPop - Marc Gravell
1
我发现使用 BlockingCollection<T> 对于我的程序(生产者/消费者)来说更好。 - Ibrahim Ahmed

5

栈已满,有16项?这怎么可能?!

在多线程环境下,这是完全可能的。

您的程序中是否使用了多个线程?如果是,SharedMemory 在进行任何更改之前应该被lock


在查询之前! - Olivier Jacot-Descombes

3
如果SharedMemory是一个Stack,并且您正在使用多线程,如果您使用的是.Net 4,则应该使用:ConcurrentStack 编辑 在第一次编辑后,根据Quartermeister的评论,这是一个更简单的工作解决方案:
    int item;
    var SharedMemory = new BlockingCollection<int>(new ConcurrentStack<int>());
    // later in the Consume part
    item = SharedMemory.Take(); // this will block until there is an item in the list
    Console.WriteLine(item);

1
ConcurrentStack<> 只有 TryPop 方法,因此如果您想要一个阻塞弹出设置(如问题所述),使用该 API 并不能使其更简单。 - Marc Gravell
@MarcGravell 没有看到阻塞弹出 :) - MBen
@MarcGravell,使用ConcurrentStack简化代码不是更好吗? - MBen
@MarcGravell 你是对的。不幸的是,你会得到一个异常,说它没有被锁定(这是真的)。如果这些集合提供像BlockingCollection<T>这样的阻塞机制就好了。 - MBen
2
你可以将ConcurrentStack包装在BlockingCollection中。BlockingCollection的默认构造函数使用ConcurrentQueue,但是还有另一个构造函数,它接受一个IProducerConsumerCollection,你可以将ConcurrentStack传递给该构造函数以获得一个阻塞的LIFO集合。 - Quartermeister
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接