Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/66.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 异步&;等待-如何等待所有任务完成?_C#_.net_Multithreading_Asynchronous_Async Await - Fatal编程技术网

C# 异步&;等待-如何等待所有任务完成?

C# 异步&;等待-如何等待所有任务完成?,c#,.net,multithreading,asynchronous,async-await,C#,.net,Multithreading,Asynchronous,Async Await,嗯。我制作了一个简单的控制台应用程序,以了解如何使所有这些工作。一旦我有了基本的大纲,那么我将把它应用到实际的应用程序中 这个想法是,我们有很多数据库调用要执行,我们知道这将需要很长时间。我们不希望(或必须)在进行下一个数据库调用之前等待一个数据库调用完成。它们可以同时运行 但是,在进行所有调用之前,我们需要执行“启动”任务。当所有调用都完成时,我们需要执行一个“已完成”的任务 这就是我现在的处境: static void Main(string[] args) { Console.Wr

嗯。我制作了一个简单的控制台应用程序,以了解如何使所有这些工作。一旦我有了基本的大纲,那么我将把它应用到实际的应用程序中

这个想法是,我们有很多数据库调用要执行,我们知道这将需要很长时间。我们不希望(或必须)在进行下一个数据库调用之前等待一个数据库调用完成。它们可以同时运行

但是,在进行所有调用之前,我们需要执行“启动”任务。当所有调用都完成时,我们需要执行一个“已完成”的任务

这就是我现在的处境:

static void Main(string[] args)
{
    Console.WriteLine("starting");
    PrintAsync().Wait();        
    Console.WriteLine("ending");  // Must not fire until all tasks are finished
    Console.Read();
}

// Missing an "await", I know. But what do I await for?
static async Task PrintAsync()
{
    Task.Run(() => PrintOne());
    Task.Run(() => PrintTwo());
}

static void PrintOne()
{
    Console.WriteLine("one - start");
    Thread.Sleep(3000);
    Console.WriteLine("one - finish");
}

static void PrintTwo()
{
    Console.WriteLine("two - start");
    Thread.Sleep(3000);
    Console.WriteLine("two - finish");
}
但无论我怎么做,
结尾总是打印得太早:

starting
ending
one - start
two - start
one - finish
two - finish

正确的做法是在
PrintOne()
完成之前启动
printwo()
。但是,如何正确地等待
PrintAsync()
完成后再执行其他操作?

您需要等待内部任务的结束:

static async Task PrintAsync()
{
    await Task.WhenAll(Task.Run(() => PrintOne()), Task.Run(() => PrintTwo()));
}

说明:
异步任务
表示等待的方法。在该方法中,您还可以等待任务。如果您不这样做,那么它只会让任务自行运行<代码>任务。运行
返回可等待的
任务。如果希望两个任务并行运行,可以使用retur值中的任务,并在waitiable方法中使用它们

编辑:实际上VisualStudio会用一条绿色的曲线来标记这段代码。当鼠标悬停在上面时,您会收到一条警告:

这应该解释为什么在任务完成之前打印“结束”

编辑2:

如果您有一组要迭代并调用异步方法以传入参数的参数,还可以使用一行select语句执行此操作:

static async Task DatabaseCallsAsync()
{
    // List of input parameters
    List<int> inputParameters = new List<int> {1,2,3,4,5};  
    await Task.WhenAll(inputParameters.Select(x => DatabaseCallAsync($"Task {x}")));
}

static async Task DatabaseCallAsync(string taskName)
{
    Console.WriteLine($"{taskName}: start");
    await Task.Delay(3000);
    Console.WriteLine($"{taskName}: finish");
}
静态异步任务数据库callsasync()
{
//输入参数列表
列表输入参数=新列表{1,2,3,4,5};
wait Task.WhenAll(inputParameters.Select(x=>DatabaseCallAsync($“Task{x}”));
}
静态异步任务数据库callasync(字符串taskName)
{
WriteLine($“{taskName}:start”);
等待任务。延迟(3000);
WriteLine($“{taskName}:finish”);
}

最后一部分与此处的操作类似。我将把Mong Zhu的答案标记为正确,因为它将引导我找到解决方案。但我也想在这里分享最终的结果,其中包括juharr评论中的优秀反馈。以下是我的想法:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("starting");
        DatabaseCallsAsync().Wait();            
        Console.WriteLine("ending"); // Must not fire until all database calls are complete.
        Console.Read();
    }

    static async Task DatabaseCallsAsync()
    {
        // This is one way to do it...
        var tasks = new List<Task>();
        for (int i = 0; i < 3; i++)
        {
            tasks.Add(DatabaseCallAsync($"Task {i}"));
        }
        await Task.WhenAll(tasks.ToArray());

        // This is another.  Same result...
        List<int> inputParameters = new List<int> { 1, 2, 3, 4, 5 };
        await Task.WhenAll(inputParameters.Select(x => DatabaseCallAsync($"Task {x}")));
    }

    static async Task DatabaseCallAsync(string taskName)
    {
        Console.WriteLine($"{taskName}: start");
        await Task.Delay(3000);
        Console.WriteLine($"{taskName}: finish");
    }
}

Task.Run
返回一个
任务
。等待任务。如果正在处理DB调用,则应使用异步方法与DB对话,因为这是IO绑定的,而不是将这些调用包装在
任务中。运行
@juharr。。。好啊那么,我该怎么做才能使我不必等待一个DB调用在下一个启动之前完成呢?@caseycrokston以完全相同的方式说。您将有一些
async
方法来执行每个DB调用,您将捕获它们的任务并将它们放入
任务中。基本上,
PrintOne
printwo
将是返回
Task
async
方法,您不需要将它们包装到
Task中。运行
。您可以使用
wait Task.Delay
而不是
Thread.Sleep
。好的,但是如果我在每个调用中使用
wait
,那么它们不是一次执行一个而不是同时执行所有调用吗?我没在脑子里想这个。哇!美好的我不知道Task.WhenAll()的事。非常感谢。我会尽快把答案标记出来。@caseycrokston不客气。我添加了一个带有文档链接的解释。希望对你有帮助谢谢。接下来,我需要弄清楚如何将静态PrintOne()和printwo()转换成一个仍然以相同方式运行的循环。@CaseyCrookston告诉我更多信息。循环应该驻留在哪里?:)如果我搞不懂,我会发表一篇新文章,并在这里添加链接。如果你有一个异步方法的参数集合,你可以使用一个简单的select语句来执行异步方法并等待它们(即使在一行中),看看我的第二次编辑。@MongZhu,谢谢!那很有帮助。问题:假设一个大的列表而不是一个小的列表,那么一种方式相对于另一种方式有速度优势吗?
starting
Task 0: start
Task 1: start
Task 2: start
Task 2: finish
Task 0: finish
Task 1: finish
ending