你能用C#线程同步发出信号并自动等待吗?

你能用C#线程同步发出信号并自动等待吗?,c#,synchronization,thread-safety,C#,Synchronization,Thread Safety,我在C#中的线程同步方面遇到一些问题。我有一个由两个线程操作的共享对象,我使用lock()使对该对象的访问互斥,但我还想根据共享对象的状态阻止每个线程。特别是当对象为空时阻塞线程A,当对象为满时阻塞线程B,当对象状态改变时,让另一个线程向阻塞线程发出信号 我尝试使用ManualResetEvent执行此操作,但遇到了一个争用条件,线程B将检测到对象已满,移动到WaitOne,线程a将进入并清空对象(向MRE发送每次访问的信号,并在对象为空时阻塞自身),然后线程a命中其WaitOne,这意味着线程

我在C#中的线程同步方面遇到一些问题。我有一个由两个线程操作的共享对象,我使用lock()使对该对象的访问互斥,但我还想根据共享对象的状态阻止每个线程。特别是当对象为空时阻塞线程A,当对象为满时阻塞线程B,当对象状态改变时,让另一个线程向阻塞线程发出信号

我尝试使用ManualResetEvent执行此操作,但遇到了一个争用条件,线程B将检测到对象已满,移动到WaitOne,线程a将进入并清空对象(向MRE发送每次访问的信号,并在对象为空时阻塞自身),然后线程a命中其WaitOne,这意味着线程A正在等待线程未满,即使它未满

我想如果我能调用一个像'SignalAndWaitOne'这样的函数,它会在等待前自动发出信号,这会阻止这种竞争条件吗

谢谢

A已经由.NET4.0提供

如果您使用的是早期版本,则可以直接使用该类


编辑:以下代码完全未经测试,不处理较小的
maxCount
值(执行此操作的典型方法是使用Monitor.Enter、Monitor.Wait和Monitor.Pulse来控制对共享队列的访问。草图:

shared object sync = new object()
shared Queue q = new Queue()

Producer()
    Enter(sync)
    // This blocks until the lock is acquired
    while(true)
        while(q.IsFull)
            Wait(sync)     
            // this releases the lock and blocks the thread 
            // until the lock is acquired again

        // We have the lock and the queue is not full.
        q.Enqueue(something)
        Pulse(sync)
        // This puts the waiting consumer thread to the head of the list of 
        // threads to be woken up when this thread releases the lock

Consumer()
    Enter(sync)
    // This blocks until the lock is acquired
    while(true)
        while(q.IsEmpty)
            Wait(sync)
            // this releases the lock and blocks the thread 
            // until the lock is acquired again

        // We have the lock and the queue is not empty.
        q.Dequeue()
        Pulse(sync)
        // This puts the waiting producer thread to the head of the list of 
        // threads to be woken up when this thread releases the lock

我不确定我在这里是否完全了解您,线程的代码示例可能会有所帮助。Joe Duffy在几年前的一篇文章中展示了一个简单的阻塞队列和有界缓冲区:我使用的是.NET的早期版本。您介意更详细地介绍一下如何使用监视器来防止上述竞争条件吗?我看ed以前在监视器上做过测试,但想不出如何防止状态检查和等待之间的竞争。添加了一个代码示例。还请注意Eric Lippert的回答,它显示了相同的模式。啊,太完美了。在等待时解锁关键部分的能力正是我所缺少的。一个关于C#监视器的快速问题。我不需要ca吗ll监控。在关键部分结束时退出(锁定)以释放锁,还是脉冲(锁定)自动释放锁?出于某种原因,我的代码似乎在没有退出的情况下工作(令我惊讶)。可能是在函数完成或线程返回并调用Enter(锁定)时释放锁第二次。为了确保我做对了,
Monitor.Wait
执行以下操作:(1)退出,(2)[等待其他人获得锁],(3)进入?@hookslaw:你会在我的草图中注意到,没有任何东西会离开关键部分,因此没有“退出”。通常在实践中,“进入”实际上是一个“锁”语句,这样编译器就会自动将“出口”放在“finally”块中。如果您确实希望代码离开循环,那么肯定应该在其中的某个地方有一个出口。@configurator:基本上,尽管细节有点复杂。每个锁都有一个“等待”线程队列和一个线程队列“就绪”线程。等待线程会退出锁(如果在该线程上多次输入锁,则可能不止一次),将线程放入等待队列,并通知就绪队列上的第一个线程进入锁。我们希望有一天有人会将刚称为“等待”的线程移动到就绪队列上,依此类推。”脉搏"将线程从等待队列移动到准备队列。@EricLippert我想知道…在调用等待之前,您不应该调用Pulse吗?据我所知,Pulse正在将等待线程移动到准备队列,然后当wait accures准备好的线程将跳转时。在您提到的场景中,可能没有准备好的线程等待,然后消费者和生产者都进入等待队列,没有人叫醒他们(基本情况是一个消费者和一个生产者???如果我错过了什么,请告诉我。。。。
shared object sync = new object()
shared Queue q = new Queue()

Producer()
    Enter(sync)
    // This blocks until the lock is acquired
    while(true)
        while(q.IsFull)
            Wait(sync)     
            // this releases the lock and blocks the thread 
            // until the lock is acquired again

        // We have the lock and the queue is not full.
        q.Enqueue(something)
        Pulse(sync)
        // This puts the waiting consumer thread to the head of the list of 
        // threads to be woken up when this thread releases the lock

Consumer()
    Enter(sync)
    // This blocks until the lock is acquired
    while(true)
        while(q.IsEmpty)
            Wait(sync)
            // this releases the lock and blocks the thread 
            // until the lock is acquired again

        // We have the lock and the queue is not empty.
        q.Dequeue()
        Pulse(sync)
        // This puts the waiting producer thread to the head of the list of 
        // threads to be woken up when this thread releases the lock