同时执行多个长时间运行的进程的最简单方法 C#

ton*_*314 0 c# multithreading async-await parallel.foreach

我有一个长期运行的工作,我需要为集合中的每个项目运行一次。

我想同时做这些工作,虽然整个程序可以等待所有工作完成。(稍后我可能会更改它,但就目前而言,我让问题保持简单)

根据已经提供的一些帮助,我得到了以下模式:

public static void DoWork()
{
    //Get a collection of items
    var items = GetMyItems();

}


private async void DoStuffWithItems(ICollection<MyItem> items)
{
    var tasks = items.Select (i => DoStuffWithItem(i));
    await Task.WhenAll(tasks);
}

private Task DoStuffWithItem(MyItem item)
{
    //LongRunningTask
    return Task.Run(async () =>
            {
                var returnObject = await LongRunningAsyncMethod(item);
            });
}
Run Code Online (Sandbox Code Playgroud)

如果我理解正确的话,这仍然一次完成每项任务 - 毫无意义。

有人建议我将 Parallel.ForEach 与 async await 结合使用 - Parallel-ForEach 模式很简单:

public static void DoWork()
{
    //Get a collection of items
    var items = GetMyItems();
    
    Parallel.ForEach(items, (item) =>
    {
        var returnObject = LongRunningMethod(item);
    }

}
Run Code Online (Sandbox Code Playgroud)

现在,如果我理解正确,我将为每个项目启动一个单独的线程来做它的事情,但是当它这样做时,主线程将等待

但是,如果我希望在 DoWork 等待 Parallel.ForEach 完成时继续执行程序的其余部分,该怎么办?

public static void DoWork()
{

//Get a collection of items
var items = GetMyItems();

DoINeedThisMethod(items);


}

private async void DoINeedThisMethod(ICollection<MyItem> items)
    {
        await Task.Run(() => {
            DoStuffWithItems(items);
        });
    }

private async void DoStuffWithItems(ICollection<MyItem> items)
{
        var newobjs = new List<MyItem>();

            Parallel.ForEach(items, (item) =>
            {
                var bc = LongRunningAsyncMethod(item);
            });
}
Run Code Online (Sandbox Code Playgroud)

感觉不太对劲——我在那里有一个异步空洞,这不是一个好习惯。但是我在这里是正确的吗?如果我理解正确的话,在 DoINeedThisMethod 中,我正在启动一个新线程(通过 Task)来执行 Parallel.ForEach 调用,但是在那里使用“wait”意味着主线程现在将继续,而 Parallel.ForEach完成。

Joh*_*nne 5

实际上,你的第一个假设

如果我理解正确的话,这仍然一次完成每项任务

很容易证明是错误的:

    private static async Task Main(string[] args)
    {

        async Task DoStuffWithItem(int i)
        {
            // long running task
            Console.WriteLine($"processing item {i} started");
            await Task.Delay(500);
            Console.WriteLine($"processing item {i} finished");
        }
        
        var items = new List<int> { 1, 2, 3 };

        var tasks = items.Select(i => DoStuffWithItem(i)).ToList();
        await Task.WhenAll(tasks);

        Console.WriteLine("\nFinished");
    }
Run Code Online (Sandbox Code Playgroud)

产生以下输出:

processing item 1 started
processing item 2 started
processing item 3 started
processing item 2 finished
processing item 1 finished
processing item 3 finished

Finished
Run Code Online (Sandbox Code Playgroud)

所以任务是并发执行的......

注意:正如下面的评论中所解释的,“ToList()”会导致立即执行 Linq 查询(立即启动任务)。如果没有 'ToList()',任务只会在执行 'await Task.WhenAll(tasks)' 时启动

  • @tonydev314:我感觉您还没有正确理解“Task.Wait”和“await”之间的区别:如果您等待任务的执行,您的代码将在此时停止(直到任务停止运行) )。但是,运行代码的线程不会被阻止。在您的示例中,GUI 线程仍会对用户和系统事件做出反应。对 Async..Await 的很好的介绍:https://blog.stephencleary.com/2012/02/async-and-await.html (2认同)
  • 还有一件事需要注意:在示例中,您使用 Linq(“Select”方法)构造“任务”实例。这有一个副作用:在评估“tasks”之前,不会执行 Linq 枚举。因此,在上面的示例中,在到达“await Task.WhenAll(tasks)”语句之前,长时间运行的任务不会启动。如果您想启动任务,请在等待任务完成之前执行更多内容,但是(当任务运行时),您需要强制执行 Linq 查询(例如,通过调用“ToList”:vartasks = items.Select (i =&gt; DoStuffWithItem(i)).ToList();' (2认同)