【问题标题】:How to determine the status of the job Async/Await如何确定作业 Async/Await 的状态
【发布时间】:2013-11-14 12:02:01
【问题描述】:

我从这里http://blog.stephencleary.com/2012/02/async-and-await.html阅读了这个指南

这里我有几个代码,但对我来说不是很清楚。

1)

public async Task DoOperationsInParallelAsync()
{
  Task[] tasks = new Task[3];
  tasks[0] = DoOperation0();
  tasks[1] = DoOperation1();
  tasks[2] = DoOperation2();

  // At this point, all three tasks are running in parallel.

  // Now, we await them all.
  await Task.WhenAll(tasks);
}

在上面我们创建了多个任务,但假设当所有任务将并行运行时,DoOperation2() 可能首先完成,DoOperation0() 和最后 DoOperation1() 完成。如果我想在控制台窗口中显示像 DoOperation2() 这样的消息,那么我该怎么做。当多个正在运行时,我如何检测哪个任务完成。

2) 当我们在 async/await 的帮助下运行任何函数时,它是作为后台线程还是前台线程运行。

3)

public async Task<int> GetFirstToRespondAsync()
{
  // Call two web services; take the first response.
  Task<int>[] tasks = new[] { WebService1(), WebService2() };

  // Await for the first one to respond.
  Task<int> firstTask = await Task.WhenAny(tasks);

  // Return the result.
  return await firstTask;
}

我不明白这个人为什么写等待第一个回复。

// 等待第一个响应。 Task firstTask = await Task.WhenAny(tasks);

为什么是第一个...为什么不是第二个,因为这里正在运行两个任务。

请指导我并消除我的困惑。谢谢

【问题讨论】:

  • 我认为您的一些困惑是由语言引起的:第一个响应并不意味着第一个条目,而是意味着先响应的将是第一个。

标签: c# asynchronous


【解决方案1】:
  1. 因为是控制台应用,所以需要等待任务完成。下面是一个如何从任务中返回字符串的示例:

    class WhenAny
    {
        public static async Task<string> GetFirstToRespondAsync()
        {
            // Call two web services; take the first response.
            Task<string>[] tasks = new[] { Task1(), Task2() };
    
            // Await for the first one to respond.
            Task<string> firstTask = await Task.WhenAny(tasks);
    
            // Return the result.
            return firstTask.Result;
        }
    
        private static async Task<string> Task1()
        {
            await Task.Delay(3000);
            return "Task1";
        }
    
        private static async Task<string> Task2()
        {
            await Task.Delay(1000);
            return "Task2";
        }
    }
    

    从 Main 函数中调用它,如下所示:

    static void Main(string[] args)
    {
        var t = WhenAny.GetFirstToRespondAsync();
        t.ContinueWith((taskName) =>
            {
                string result = taskName.Result;
                Console.WriteLine("Result: " + result);
            });
    
        t.Wait();
        Console.ReadLine();
    }
    

    这应该会返回最先完成的任务,您可以从Task.Result 访问该信息

  2. 等待方法本身并不会创建额外的线程。它的作用是创建一个回调以避免阻塞当前线程(通常用于不阻塞 UI 线程)。

  3. WhenAny 在最早完成的操作返回时返回。这并不意味着您提供的列表中的第一个。因此,上面的代码将始终显示 1000,即使它是第二个任务。

为了完整起见,这里与WhenAll 相同:

class WhenAll
{

    public static async Task<string[]> WaitForAllAsync()
    {
        // Call two web services; take the first response.
        Task<string>[] tasks = new[] { Task1(), Task2(), Task3() };

        // Wait for a tasks
        string[] results = await Task.WhenAll(tasks);

        // Return the result.
        return results;
    }

    private static async Task<string> Task1()
    {
        await Task.Delay(3000);
        return "Task1";
    }

    private static async Task<string> Task2()
    {
        await Task.Delay(1000);
        return "Task2";
    }

    private static async Task<string> Task3()
    {
        await Task.Delay(5000);
        return "Task3";
    }

}

并称它为:

static void Main(string[] args)
{
     var t = WhenAll.WaitForAllAsync();
     t.ContinueWith((task) =>
     {
         string[] result = task.Result;
         foreach(string taskname in result)
         {
             Console.WriteLine("Result: " + taskname);
         }
     });

     t.Wait();
     Console.ReadLine();

}

【讨论】:

  • 你能指导我完成后如何例行命名。在这里,我们正在执行 2 个例程 Task1 和 Task2。所以从按钮点击我怎么能显示像Task2()这样的例程名称已完成。谢谢
  • 适用于WhenAll 还是WhenAny?
  • 这里你正在运行两个任务,我想当任何任务完成时,任务名称将返回到它的调用环境。
【解决方案2】:

您可以对将更新进度的每个任务使用 ContinueWith 方法。

所以...

foreach (var t in tasks)
{
   t.ContinueWith(Console.WriteLine("Hey I am done"));
}

【讨论】:

    【解决方案3】:

    1) 传入回调函数

    public async Task DoOperationsInParallelAsync(Action<Task<int>> completed)
    {
        var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
        var tasks = new[] { DoOperation0(), DoOperation1(), DoOperation2() };
    
        var completedTasks = tasks.Select(x => x.ContinueWith(completed, uiScheduler)).ToArray();
    
        await Task.WhenAll(completedTasks);
    }
    
    private async void Button1_Click(object sender, EventArgs e)
    {
        await DoOperationsInParallelAsync(t => {
            Label1.Text = string.Format("Task finished with {0}", t.Result);
        });
    }
    

    2) Task 将在线程池上运行,除非您将其指定为长时间运行或提供TaskScheduler

    3) 当只有第一个结果很重要(计算是冗余的或时间敏感的)时,例如从反映相似数据的不同供应商那里获取股票价格。

    【讨论】:

    • 感谢您的回复。您能否发布完整的代码,说明如何调用 DoOperationsInParallelAsync() 我们可以将另一个函数名称引用作为回调传递。以及从回调如何在完成时显示例程,如“DoOperation1() Done”
    • 我添加了一个带有按钮和标签的示例。
    猜你喜欢
    • 2020-09-10
    • 2020-08-23
    • 1970-01-01
    • 2020-03-08
    • 1970-01-01
    • 1970-01-01
    • 2019-07-09
    • 2019-10-27
    • 1970-01-01
    相关资源
    最近更新 更多