如果队列不为空而不使用 while(true) 是否可以执行代码?

Is it possible to execute codes if a queue is not empty without using while(true)?

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

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

这个 while-sleep "Query Mode" 看起来很愚蠢....大多数时候 cpu 只是保持上下文切换 nothing.But 很多人都在这样做。有没有更好的办法? "interrupt" 或 "trigger" 之类的方式?

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

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

旋转等待

您可以使用 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();

正在启动一个任务来处理队列

如果队列中的数据以短暂的突发形式出现,随后出现较长的中断,最好不要 运行 在无事可做时在后台进行任何操作,只启动一个将清空的任务队列中有项目时。在较长的休息时间,您可以 return 将处理队列的线程转移到线程池,在那里它可以用于更重要的事情。

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

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,它的优点是您不必自己手动锁定。