如何在 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# 中运行多个任务并在完成这些任务时获取事件?的主要内容,如果未能解决你的问题,请参考以下文章

如何在基于 C# 的 Windows 服务中处理以不同时间间隔并行运行的多个任务?

在异步任务运行时显示警报框并在任务完成时删除?

C#异步编程概念和使用

如何将我的 c# 程序作为计划任务运行

如何只处理最后一个任务并在c#中丢弃上一个任务

烧瓶中写入数据库的后台任务