如何在 c# 中运行多个任务并在完成这些任务时获取事件?
Posted
技术标签:
【中文标题】如何在 c# 中运行多个任务并在完成这些任务时获取事件?【英文标题】:How to run multiple tasks in c# and get an event on complete of these tasks? 【发布时间】:2015-10-20 01:46:02 【问题描述】:我正在重新运行 Task
完成后。下面是我在应用程序的Application_Start
中调用的函数。
private void Run()
Task t = new Task(() => new XyzServices().ProcessXyz());
t.Start();
t.ContinueWith((x) =>
Thread.Sleep(ConfigReader.CronReRunTimeInSeconds);
Run();
);
我想运行多个任务,编号将从 web.config 应用设置中读取。
我正在尝试这样的事情,
private void Run()
List<Task> tasks = new List<Task>();
for (int i = 0; i < ConfigReader.ThreadCount - 1; i++)
tasks.Add(Task.Run(() => new XyzServices().ProcessXyz()));
Task.WhenAll(tasks);
Run();
这样做的正确方法是什么?
【问题讨论】:
如果您不想深入构建自己的 taskrunner/扩展该类,我可能会使用tasks.First().Wait();
等待第一个任务结束,然后使用带有线程的 while 循环.Sleep 观察剩余任务的状态。
您想在预定的时间间隔内重复运行一段代码吗?听起来确实是Timer
存在的确切原因。当然,如果你想坚持这一点,只需将你的 continuation 绑定到 Task.WhenAll
的结果,一切都会像以前一样工作。
您需要await Task.WhenAll(tasks);
或Task.WaitAll(tasks);
。一个是异步调用,应该等待,另一个是可以在同步上下文中调用的普通函数。当然你也可以去丑的Task.WhenAll(tasks).Wait();
【参考方案1】:
我相信您正在寻找:
Task.WaitAll(tasks.ToArray());
https://msdn.microsoft.com/en-us/library/dd270695(v=vs.110).aspx
【讨论】:
你错过了给ToArray()
的电话
开枪!正是我所需要的。
@Amit 此处使用的列表与示例中使用的数组之间存在差异,谢谢 :)
WaitAll
是 pre async
await
方法,它不必要地占用了主线程。 ***.com/questions/6123406/waitall-vs-whenall
@Yasser,这会起作用,但你应该使用Task.WhenAll
,查一下。【参考方案2】:
如果您想等待所有任务完成然后重新启动它们,Marks 的答案是正确的。
但如果您希望 ThreadCount 个任务在任何时候都在运行(只要其中任何一个任务结束就开始一个新任务),那么
void Run()
SemaphoreSlim sem = new SemaphoreSlim(ConfigReader.ThreadCount);
Task.Run(() =>
while (true)
sem.Wait();
Task.Run(() => /*Your work*/ )
.ContinueWith((t) => sem.Release(); );
);
【讨论】:
为什么是内部Task.Run
?您正在占用一个线程阻塞,而另一个用于同步运行某些东西。只需使用sem.Wait(); try RunWhatever(); finally sem.Release();
。
@Luaan 否 这不是我想要对我的代码执行的操作。我不是在等待一项任务完成然后开始一项新任务。每当其中一个完成时,我都会创建一个新任务。所以总会有 ThreadCount 个任务在运行..
Doesn't Tasks.WhenAll 使用 ThreadPool 自动运行“最佳”数量的任务,同时将其他任务排队?
在异步上下文中,我强烈希望在信号量上使用WaitAsync()
。而且完全没有理由不在这里切换到异步上下文。
@RoyT。不确定,但Parallel.ForEach 在这种情况下会更有意义。 TPL 是围绕并行运行任务构建的,除非您的工作是异步的,否则无需将其与 Tasks.WhenAll
之类的异步方法混合使用。【参考方案3】:
如果你想一个接一个地运行任务,
await Task.Run(() => new XyzServices().ProcessXyz());
await Task.Delay(ConfigReader.CronReRunTimeInSeconds * 1000);
如果您想在任务调度程序允许的情况下同时运行它们,
await Task.WhenAll(new[]
Task.Run(() => new XyzServices().ProcessXyz()),
Task.Run(() => new XyzServices().ProcessXyz())
);
所以,你的方法应该是这样的,
private async Task Run()
var tasks =
Enumerable.Range(0, ConfigReader.ThreadCount)
.Select(i => Task.Run(() => new XyzServices().ProcessXyz()));
await Task.WhenAll(tasks);
【讨论】:
我之前读到过,等待任务并不是开始执行的,它们实际上是立即开始的。它是否正确?这会使你的第一个陈述变得复杂。 如果您调用的函数本身是异步的,则不需要将它们包装在Task.Run
中。但是,重要的是要意识到异步任务将立即在当前线程上启动,并且只有在运行到异步调用本身时才切换上下文。对于 CPU 繁重的函数,您当然应该将它们包装在 Task.Run
中,对于快速切换到异步调用(通常是 IO)的函数,不包装它们可以获得更好的性能。以上是关于如何在 c# 中运行多个任务并在完成这些任务时获取事件?的主要内容,如果未能解决你的问题,请参考以下文章