【问题标题】:Is it possible to execute codes if a queue is not empty without using while(true)?如果队列不为空,是否可以在不使用 while(true) 的情况下执行代码?
【发布时间】:2018-05-12 21:47:00
【问题描述】:

现在或自从我学习编程以来,我正在使用以下代码来执行此操作:

  ConcurrentQueue<MyPkg> messageQueue= new ConcurrentQueue<MyPkg>;
  private void Post()
        {
            while (true)
            {
                if (messageQueue.IsEmpty)
                {
                    //Post to server
                }
                Thread.Sleep(1);
            }
        }

这种睡眠时的“查询模式”看起来很愚蠢....大多数时候cpu只是保持上下文切换是徒劳的。但是很多人都在这样做。有没有更好的办法? “中断”或“触发”之类的方式?

【问题讨论】:

  • 解决方案(可能太重)可能是BlockingCollection
  • 除此之外,除非它是为线程安全访问而设计的类型,否则这很可能已经被破坏了。
  • 然后是blocking queue
  • 代码中的任何内容都不会对 cpu 需要多少上下文切换产生太大影响——而且这不是没有原因的,总是有其他进程需要 CPU 时间。话虽如此,通常*有更好的方法让一个线程等待另一个线程(或进程)发出信号,例如,当有数据要处理时。您是否可以控制数据放入队列时会发生什么?
  • 只需按照上面的建议将您的并发队列包装到 BlockingCollection 中,为什么要重新发明轮子?

标签: c# while-loop queue infinite-loop


【解决方案1】:

有许多不同的方法可以解决您的问题。哪个最好取决于您的情况。在这里提供所有可能的选项是不可能的,特别是因为一些解决方案可能变得相当复杂。最后,它是关于了解您的选择并将它们用作解决方案的构建块。

这里有一些你可以做的事情。请注意,这些是非常基本的实现,没有考虑取消和超时。您必须根据自己的要求将它们添加到组合中。

旋转等待

您可以使用SpinWait 等到您的队列有任何项目。 SpinWait 当您期望等待时间相当短时是一个不错的选择,因为在这些情况下它会旋转而不是导致线程上下文切换。 SpinWait 会在等待时间过长时自动锁定,但您仍然不应该将其用于更长的等待时间。

一个例子是:

while (true)
{
    while (!messageQueue.IsEmpty)
    {
        //Post to server
    }
    SpinWait.SpinUntil(() => !messageQueue.IsEmpty);
}

等待句柄

另一种选择是使用等待句柄,该句柄可在项目入队时设置,并阻塞接收线程。这通常会导致线程切换,因此当数据进来的频率不是太高时这是一个不错的选择,但处理非常昂贵或其他线程可能会更好地利用处理时间而队列中没有任何内容。

等待句柄的一个不错的选择是AutoResetEvent

AutoResetEvent waitHandle = new AutoResetEvent(false);
while (true)
{
    waitHandle.WaitOne();
    while (!messageQueue.IsEmpty)
    {
        //Post to server
    }
}

//When you enqueue your items...

messageQueue.Enqueue(item);
waitHandle.Set();

启动一个任务来处理队列

如果队列中的数据以短暂的突发形式进入,然后是较长的中断,则最好不要在无事可做时在后台运行任何东西,而仅在有项目时启动将清空队列的任务在里面。在较长的休息时间,您可以将处理队列的线程返回到线程池,在那里它可以用于更重要的事情。

您可以按需启动Task 并使用bool 变量来跟踪您的处理任务是否正在运行。

bool isProcessing = false;
object processingSync = new object();

//...

void ProcessQueue() 
{
    bool shouldContinue;
    do
    {
        object item; //Set thhis to the right type
        lock (processingSync)
        {
            //Note: you wouldn't actually need a concurrent queue here,
            //since you are locking during enqueue and dequeue
            shouldContinue = messageQueue.TryDequeue(out item);
            if (!shouldContinue)
            {
                isProcessing = false;
            }
        }
        //Process item
    }
    while (shouldContinue);
}

//When you enqueue your items...

lock (processingSync)
{
    messageQueue.Enqueue(item);
    if (!isProcessing)
    {
        Task.Run(ProcessQueue);
        isProcessing = true;
    }
}

当您提前知道队列中有多少项目时,您还可以使用BlockingCollection,其优点是您不必自己手动锁定。

【讨论】:

    猜你喜欢
    • 2019-11-03
    • 2021-09-21
    • 1970-01-01
    • 1970-01-01
    • 2011-10-30
    • 1970-01-01
    • 2019-02-12
    • 2017-01-05
    • 2011-07-24
    相关资源
    最近更新 更多