如何排队使用C#在后台按顺序执行的委托? [英] How to queue up delegates to be executed in series in the background with C#?

查看:624
本文介绍了如何排队使用C#在后台按顺序执行的委托?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从游戏循环中开始应在后台执行的工作,但不应阻塞游戏循环.

From a game loop i want to start work in the background that should be executed one after another but should not block the game loop.

因此理想情况下,可以像这样使用类BackgroundQueue:

So ideally a class BackgroundQueue that could be used like this:

BackgroundQueue myQueue = new BackgroundQueue();
//game loop called 60 times per second
void Update()
{
    if (somethingHappens)
    {
        myQueue.Enqueue(() => MethodThatTakesLong(someArguments))
    }   
}

.NET中是否有适用于该场景的现成类?还是有人知道如何实现BackgroundQueue类的好例子?

Is there a ready made class in .NET that works for the scenario? Or does someone know a good example on how to implement the BackgroundQueue class?

如果类可以报告它当前是否正在做某事以及有多少个委托人在排队……

A nice to have would be if the class could report whether it's currently doing something and how many delegates are queued up...

推荐答案

一种解决方案来自出色的线程化C#电子书.在基本结构部分中,作者几乎完全按照您的要求例子.

One solution is from the excellent Threading in C# E-book. In their section on basic structures, the author makes almost exactly what you're looking for in an example.

点击该链接,然后向下滚动到生产者/消费者"队列.

Follow that link and scroll down to Producer/consumer queue.

在后面的部分中,他将解决 ConcurrentQueue 工作也很好,除了在高度并行的情况下,在所有情况下它的性能都较差.但是对于您的低负载情况,最好是使某些东西容易工作.我对文档中的声明没有经验,但是您可以在那里进行评估.

In a later section, he addresses that while ConcurrentQueue would work fine too, it performs worse in all cases EXCEPT in highly-concurrent scenarios. But for your low-load case, it may be better just to get something working easily. I don't have personal experience with the claim from the document, but it's there for you to evaluate.

我希望这会有所帮助.

Edit2:根据Evk的建议(谢谢!), BlockingCollection 类看起来像您想要的.默认情况下,它在内部使用ConcurrentQueue.我特别喜欢 CompleteAdding 方法以及使用该功能的能力带有它的CancellationTokens.事物阻塞时,并非总是能够正确解决关机"情况,但这在IMO上是正确的.

upon Evk's suggestion (thanks!), the BlockingCollection class looks like what you want. By default it uses a ConcurrentQueue under the hood. I particularly like the CompleteAdding method, as well as the ability to use CancellationTokens with it. "Shutdown" scenarios are not always correctly accounted for when things are blocking, but this does it right IMO.

根据要求,提供有关如何将其与BlockingCollection一起使用的示例.我使用foreachGetConsumingEnumerable使其对于问题的消费者方面更加紧凑:

Edit 3: As requested, a sample of how this would work with a BlockingCollection. I used the foreach and GetConsumingEnumerable to make this even more compact for the consumer side of the problem:

using System.Collections.Concurrent;
private static void testMethod()
{
  BlockingCollection<Action> myActionQueue = new BlockingCollection<Action>();
  var consumer = Task.Run(() =>
  {
    foreach(var item in myActionQueue.GetConsumingEnumerable())
    {
      item(); // Run the task
    }// Exits when the BlockingCollection is marked for no more actions
  });

  // Add some tasks
  for(int i = 0; i < 10; ++i)
  {
    int captured = i; // Imporant to copy this value or else
    myActionQueue.Add(() =>
    {
      Console.WriteLine("Action number " + captured + " executing.");
      Thread.Sleep(100);  // Busy work
      Console.WriteLine("Completed.");
    });
    Console.WriteLine("Added job number " + i);
    Thread.Sleep(50);
  }
  myActionQueue.CompleteAdding();
  Console.WriteLine("Completed adding tasks.  Waiting for consumer completion");

  consumer.Wait();  // Waits for consumer to finish
  Console.WriteLine("All actions completed.");
}

我在Sleep()调用中添加了内容,以便您可以看到在添加其他内容的同时添加了其他内容.您还可以选择启动任意数量的consumer lambda(只需将其称为Action,然后多次启动Action)或加法循环.而且,您随时可以调用集合中的Count来获取未运行的任务数.如果该值不为零,则可能是您的生产者任务正在运行.

I added in the Sleep() calls so that you can see that things are added while other things are being consumed. You can also choose to launch any number of that consumer lambda (just call it an Action, then launch the Action multiple times) or the addition loop. And at any time you can call Count on the collection to get the number of tasks that are NOT running. Presumably if that's non-zero, then your producer Tasks are running.

这篇关于如何排队使用C#在后台按顺序执行的委托?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆