C# &引用;如果两个线程正在使用Pulse并等待交互,这可能会导致死锁;

C# &引用;如果两个线程正在使用Pulse并等待交互,这可能会导致死锁;,c#,.net,multithreading,deadlock,monitor,C#,.net,Multithreading,Deadlock,Monitor,基本上,load()是针对生产者的(只有一个调度程序线程加载_tickQueue),卸载是针对消费者的(只有一个专用线程执行该函数)_tickQueue是一个受锁保护的常规队列(我将其本身用作lock()的参数)。令人惊讶的是,它导致了僵局 public void Load(Tick tick) { lock (_tickQueue) { while (_tickQueue.Count >= CapSize)

基本上,load()是针对生产者的(只有一个调度程序线程加载_tickQueue),卸载是针对消费者的(只有一个专用线程执行该函数)_tickQueue是一个受锁保护的常规队列(我将其本身用作lock()的参数)。令人惊讶的是,它导致了僵局

    public void Load(Tick tick)
    {
        lock (_tickQueue)
        {
            while (_tickQueue.Count >= CapSize)
            {
                Monitor.Wait(_tickQueue);
            }

            _tickQueue.Enqueue(tick);
            if (!_receivedTickCounts.ContainsKey(tick.Underlier))
            {
                _receivedTickCounts.Add(tick.Underlier, 0);
            }
            Console.WriteLine("Received {1} ticks for {0}", tick.Underlier, ++_receivedTickCounts[tick.Underlier]);
            Monitor.Pulse(_tickQueue);
        }
    }

    private void Unload()
    {
        while (true)
        {
            try
            {
                Tick tick;
                lock (_tickQueue)
                {
                    while (_tickQueue.Count == 0)
                    {
                        Monitor.Wait(_tickQueue);
                    }

                    tick = _tickQueue.Dequeue();
                    Monitor.Pulse(_tickQueue);
                }

                Persist(tick);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }
    }
标题中的注释可在此处找到:

我对“重要”一段的理解是:监视器类不维护状态(ResetEvent的方式)意味着死锁。给出了一个具体的例子:当两个线程使用Pulse和Wait进行交互时,如果一个线程在另一个线程不在等待队列上时发出脉冲,则会发生死锁

是否有人能明确指出(例如,给出死锁发生的场景)我在程序中哪里做错了?我看不到任何可能导致僵局的情况。 谢谢

============================编辑====================

具体地说,我想知道为什么monitor的以下编码模式突然不起作用——必须与.net中的monitor实现相关

lock
    while(wait condition is met)
    {
        wait()
    }

    // critical section: doing work

    signal();// or broadcast()
unlock

我怀疑你对这两种方法都施加了无休止的等待。您正在使用While循环不断地检查条件来包围监视方法调用。对于倾覆和_tickQueue.Count的某些值,Load()和Unload()方法都将永远等待。这里不明显的是倾覆的价值,它是恒定的还是变化的?tickQueue线程安全吗

如果我们在tick=_tickQueue.Dequeue()上出现错误怎么办;在Unload()中,_tickQueue.Count达到0,并且Load()方法正在等待()?Load()将永远等待

我会避免让你的消费者方法脉冲通知生产者方法它已经准备好进行更多的工作。您的消费者应该只在没有更多工作要做时等待(队列为空)。您的制作人更适合控制其自己的工作日程,并在新工作排队时触发消费者。为什么不把制作人放在一张桌子上

最后,我认为提供的代码只是提供了太多的失败点。我可以建议一个替代的实施方案吗?这使用了线程安全的ConcurrentQueue集合,并消除了讨论的问题

public class StackOverflowMonitorExample
{

    ConcurrentQueue<Tick> _tickQueue = new ConcurrentQueue<Tick>();
    object locker = new object();
    bool stopCondition = false;

    public void Load(Tick tick)
    {
        _tickQueue.Enqueue(tick);

        lock (locker)
        {
            Monitor.Pulse(locker);
        }

    }

    private void Unload()
    {
        while (!stopCondition)
        {
            try
            {
                Tick nextWorkItem = null;

                _tickQueue.TryDequeue(out nextWorkItem);

                if (nextWorkItem != null)
                {
                    Persist(nextWorkItem);
                }
                else
                {
                    lock (locker)
                    {
                        Monitor.Wait(locker);
                    }
                }

            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }
    }
}
公共类StackOverflowMonitore示例
{
ConcurrentQueue _tickQueue=新的ConcurrentQueue();
对象锁定器=新对象();
bool stopCondition=false;
公共空荷载(勾号)
{
_ticklue.排队(tick);
锁(储物柜)
{
监视器。脉冲(锁定器);
}
}
私有无效卸载()
{
while(!stopCondition)
{
尝试
{
勾选nextWorkItem=null;
_tickQueue.TryDequeue(out nextWorkItem);
if(nextWorkItem!=null)
{
持久化(nextWorkItem);
}
其他的
{
锁(储物柜)
{
监视器。等待(储物柜);
}
}
}
捕获(例外e)
{
控制台写入线(e);
}
}
}
}

这消除了较大的锁定部分,并消除了消费者和生产者之间的大部分信号。制作人将只向队列中添加新项目,并使用Pulse()通知新工作可用。只要项目仍在队列中且未满足停止条件,使用者将循环并继续工作。如果队列计数达到0,则消费者将等待新的队列条目。

为什么不使用
阻止收集
@西蒙·文森你好,西蒙。谢谢你的评论。我相信有一百万种方法可以完成这项任务。我想知道为什么我的方法行不通。让我们把这看作是一个技术练习,通过它我可以了解更多关于线程的知识。请为您的问题找到一个更有意义的标题!