c# - AutoResetEvent 和多个 Set

标签 c# data-structures concurrency stack autoresetevent

我正在尝试围绕堆栈设计一种数据结构,该结构会阻塞直到堆栈有可用项。我尝试使用 AutoResetEvent,但我想我误解了同步过程的工作原理。基本上,看下面的代码,我试图在没有可用的情况下从堆栈中弹出。

似乎 AutoResetEvent 的行为就像一个信号量。那是对的吗?我可以去掉 BlockingStack.Get() 中的 Set() 并完成它吗?或者这会导致我只使用我的堆栈项目之一的情况。

public class BlockingStack
{
    private Stack<MyType> _internalStack;
    private AutoResetEvent _blockUntilAvailable;

    public BlockingStack()
    {
        _internalStack = new Stack<MyType>(5);
        _blockUntilAvailable = new AutoResetEvent(false);

        for (int i = 0; i < 5; ++i)
        {
            var obj = new MyType();
            Add(obj);
        }
    }

    public MyType Get()
    {
        _blockUntilAvailable.WatiOne();

        lock (_internalStack)
        {
            var obj = _internalStack.Pop();
            if (_internalStack.Count > 0)
            {
                _blockUntilAvailable.Set(); // do I need to do this?
            }

            return obj;
        }
    }

    public void Add(MyType obj)
    {
        lock (_internalStack)
        {
            _internalStack.Push(obj);
            _blockUntilAvailable.Set();
        }
    }
}

我的假设是,当一个线程通过 WaitOne() 函数调用时,所有等待线程的 AutoResetEvent 都会重置。但是,似乎有多个线程进入。除非我在某处搞砸了我的逻辑。

编辑:这是针对 Silverlight 的。

最佳答案

除非您只是想了解线程的工作原理,否则最好使用阻塞集合。这将为您提供一个由堆栈支持的阻塞集合:

ConcurrentStack<SomeType> MyStack = new ConcurrentStack<SomeType>();
BlockingCollection<SomeType> SharedStack = new BlockingCollection<SomeType>(MyStack)

然后您可以以线程安全的方式访问它,并为您正确完成所有阻塞。参见 here

您可以通过调用 sharedStack.Take() 来使用 sharedStack,这将在获取时阻塞,直到有东西可以从堆栈中获取。


编辑: 花了我一段时间(并尝试了两次),但我认为我已经解决了你的问题。

考虑一个空堆栈,其中有 3 个线程在等待事件。

调用Add,栈中有1个对象,允许1个线程通过事件。

立即再次调用 Add。

第一个线程现在等待从 Add 获取锁。

Add 将第二个对象添加到堆栈并让另一个线程处理该事件。

现在堆栈上有两个对象,事件中有 2 个线程,都在等待锁。

First Get 线程现在获取锁并弹出。看到堆栈上仍然有一个对象并调用 SET。

允许第三个线程通过事件。

第二个 Get 线程现在获取锁并弹出。在堆栈中看不到任何内容,也不调用 set。

但是。太晚了。第三个线程已经被允许通过,所以当第二个线程放弃锁时,第三个线程尝试从空堆栈中弹出并抛出。

关于c# - AutoResetEvent 和多个 Set,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8539269/

相关文章:

c# - 防止自定义网络爬虫被拦截

java - 链表的头节点未更新

c++ - 如何在添加后 30 秒内从链表中删除项目?

java - 我应该把我的 ThreadLocals 放在一个 Spring 注入(inject)的单例中吗?

java - 并发跳过列表?也就是说,不是 ConcurrentSkipListSet

c# - WPF - 如何将转换器应用于所有 DataGridTextColumn?

c# - .net 移动文件失去原始和父文件夹安全权限

c# - 如何将位图更改为另一种随机颜色?

c++ - 使用 _beginthreadx 将结构传递给线程

c - 当我 MPI_Send 到一个已经完成的进程时会发生什么?