如何使用C#在后台将要串行执行的委托排队?
从游戏循环开始,我想在后台开始工作,应该一个接一个地执行,但不应该阻止游戏循环 理想情况下,类如何使用C#在后台将要串行执行的委托排队?,c#,.net,multithreading,task,backgroundworker,C#,.net,Multithreading,Task,Backgroundworker,从游戏循环开始,我想在后台开始工作,应该一个接一个地执行,但不应该阻止游戏循环 理想情况下,类BackgroundQueue可以这样使用: BackgroundQueue myQueue = new BackgroundQueue(); //game loop called 60 times per second void Update() { if (somethingHappens) { myQueue.Enqueue(() => MethodThatT
BackgroundQueue
可以这样使用:
BackgroundQueue myQueue = new BackgroundQueue();
//game loop called 60 times per second
void Update()
{
if (somethingHappens)
{
myQueue.Enqueue(() => MethodThatTakesLong(someArguments))
}
}
在.NET中是否有一个现成的类适用于该场景?或者有人知道如何实现BackgroundQueue
类的好例子吗
如果类可以报告当前是否正在做某事以及排队的代理数,那么这将是一个很好的选择 一个解决方案来自优秀的团队。在他们关于基本结构的部分中,作者几乎完全符合您的要求 按照该链接向下滚动到生产者/消费者队列 在后面的一节中,他指出,虽然它也可以正常工作,但除了在高度并发的场景中,它在所有情况下的性能都会更差。但是对于低负载的情况,最好是让某些东西轻松工作。我对文件中的索赔没有个人经验,但可以由你来评估 我希望这有帮助 Edit2:根据Evk的建议(谢谢!),这门课看起来就像你想要的。默认情况下,它在引擎盖下使用ConcurrentQueue。我特别喜欢这个方法,以及使用CancellationTokens的能力。当事情阻塞时,“关机”场景并不总是正确的,但在我看来这是正确的 编辑3:根据要求,这是一个如何与BlockingCollection一起工作的示例。我使用了
foreach
和getconsumineGenumerable
使问题的消费者端更加紧凑:
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.");
}
使用System.Collections.Concurrent;
私有静态void testMethod()
{
BlockingCollection myActionQueue=新建BlockingCollection();
var consumer=Task.Run(()=>
{
foreach(myActionQueue.GetConsumingEnumerable()中的变量项)
{
item();//运行任务
}//当BlockingCollection标记为不再执行任何操作时退出
});
//添加一些任务
对于(int i=0;i<10;++i)
{
int captured=i;//复制此值或
myActionQueue.Add(()=>
{
Console.WriteLine(“操作编号”+捕获+“执行”);
线程。睡眠(100);//繁忙的工作
Console.WriteLine(“已完成”);
});
Console.WriteLine(“添加的作业编号”+i);
睡眠(50);
}
myActionQueue.CompleteAdding();
Console.WriteLine(“已完成添加任务。正在等待使用者完成”);
consumer.Wait();//等待consumer完成
Console.WriteLine(“所有操作已完成”);
}
我在Sleep()调用中添加了一些内容,这样您就可以看到在添加其他内容的同时也添加了一些内容。您还可以选择启动任意数量的
消费者
lambda(只需将其称为操作
,然后多次启动操作
)或添加循环。您可以随时对集合调用Count
,以获取未运行的任务数。假设这不是零,那么您的生产者任务正在运行。一个解决方案是来自卓越。在他们关于基本结构的部分中,作者几乎完全符合您的要求
按照该链接向下滚动到生产者/消费者队列
在后面的一节中,他指出,虽然它也可以正常工作,但除了在高度并发的场景中,它在所有情况下的性能都会更差。但是对于低负载的情况,最好是让某些东西轻松工作。我对文件中的索赔没有个人经验,但可以由你来评估
我希望这有帮助
Edit2:根据Evk的建议(谢谢!),这门课看起来就像你想要的。默认情况下,它在引擎盖下使用ConcurrentQueue。我特别喜欢这个方法,以及使用CancellationTokens的能力。当事情阻塞时,“关机”场景并不总是正确的,但在我看来这是正确的
编辑3:根据要求,这是一个如何与BlockingCollection一起工作的示例。我使用了foreach
和getconsumineGenumerable
使问题的消费者端更加紧凑:
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.");
}
使用System.Collections.Concurrent;
私有静态void testMethod()
{
BlockingCollection myActionQueue=新建BlockingCollection();
var consumer=Task.Run(()=>
{
foreach(myActionQueue.GetConsumingEnumerable()中的变量项)
{
item();//运行任务
}//当BlockingCollection标记为不再执行任何操作时退出
});
//添加一些任务
对于(int i=0;i<10;++i)
{
int captured=i;//复制此值或
myActionQueue.Add(()=>
{
Console.WriteLine(“操作编号”+捕获+“执行”);
线程。睡眠(100);//繁忙的工作
Console.WriteLine(“已完成”);
});
Console.WriteLine(“添加的作业编号”+i);
睡眠(50);
}
myActionQueue.CompleteAdding();
Console.WriteLine(“已完成添加任务。正在等待使用者完成”);
consumer.Wait();//等待consumer完成
Console.WriteLine(“所有操作已完成”);
}
我在Sleep()调用中添加了一些内容,这样您就可以看到在添加其他内容的同时也添加了一些内容。您还可以选择启动任意数量的消费者
lambda(只需将其称为操作
,然后多次启动操作
)或添加循环。您可以随时对集合调用Count
,以获取未运行的任务数。假设这是非零,那么您的生产者任务正在运行。这个怎么样
void Main()
{
var executor = new MyExecutor();
executor.Execute(()=>Console.WriteLine("Hello"));
executor.Execute(()=>Console.WriteLine(","));
executor.Execute(()=>Console.WriteLine("World"));
}
public class MyExecutor
{
private Task _current = Task.FromResult(0);
public void Execute(Action action)
{
_current=_current.ContinueWith(prev=>action());
}
}
UPD
更新代码。现在我们可以得到动作的数量,从不同线程推送,等等
void Main()
{
var executor = new MyExecutor();
executor.Execute(() => Console.WriteLine("Hello"));
executor.Execute(() => Thread.Sleep(100));
executor.Execute(() => Console.WriteLine(","));
executor.Execute(() => { throw new Exception(); });
executor.Execute(() => Console.WriteLine("World"));
executor.Execute(() => Thread.Sleep(100));
executor.WaitCurrent();
Console.WriteLine($"{nameof(MyExecutor.Total)}:{executor.Total}");
Console.WriteLine($"{nameof(MyExecutor.Finished)}:{executor.Finished}");
Console.WriteLine($"{nameof(MyExecutor.Failed)}:{executor.Failed}");
}
public class MyExecutor
{
private Task _current = Task.FromResult(0);
private int _failed = 0;
private int _finished = 0;
private int _total = 0;
private object _locker = new object();
public void WaitCurrent()
{
_current.Wait();
}
public int Total
{
get { return _total; }
}
public int Finished
{
get { return _finished; }
}
public int Failed
{
get { return _failed; }
}
public void Execute(Action action)
{
lock (_locker) // not sure that lock it is the best way here
{
_total++;
_current = _current.ContinueWith(prev => SafeExecute(action));
}
}
private void SafeExecute(Action action)
{
try
{
action();
}
catch
{
Interlocked.Increment(ref _failed);
}
finally
{
Interlocked.Increment(ref _finished);
}
}
}
这个怎么样
void Main()
{
var executor = new MyExecutor();
executor.Execute(()=>Console.WriteLine("Hello"));
executor.Execute(()=>Console.WriteLine(","));
executor.Execute(()=>Console.WriteLine("World"));
}
public class MyExecutor
{
private Task _current = Task.FromResult(0);
public void Execute(Action action)
{
_current=_current.ContinueWith(prev=>action());
}
}
UPD
更新的化学需氧量