如何排队使用C#在后台按顺序执行的委托? [英] How to queue up delegates to be executed in series in the background with 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一起使用的示例.我使用foreach
和GetConsumingEnumerable
使其对于问题的消费者方面更加紧凑:
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屋!