异步方法

异步方法

问题描述:

这是AsyncMethods类的样子:异步方法

public class AsyncMethods 
{ 
    public static async Task<double> GetdoubleAsync() 
    { 
     Console.WriteLine("Thread.CurrentThread.ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId); 
     await Task.Delay(1000); 
     return 80d; 
    } 
    public static async Task<string> GetStringAsync() 
    { 
     Console.WriteLine("Thread.CurrentThread.ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId); 
     await Task.Delay(1000); 
     return "async"; 
    } 
    public static async Task<DateTime> GetDateTimeAsync() 
    { 
     Console.WriteLine("Thread.CurrentThread.ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId); 
     await Task.Delay(1000); 
     return DateTime.Now; 
    } 
} 

这是我的主要方法是什么样子:

static void Main(string[] args) 
{ 
    while (Console.ReadLine() != "exit") 
    { 
     Console.WriteLine("Thread.CurrentThread.ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId); 
     DateTime dt = DateTime.Now; 
     var res = GetStuffAsync().Result; 
     var ts = DateTime.Now - dt; 
     Console.WriteLine(res); 
     Console.WriteLine("Seconds taken: " + ts.Seconds + " milliseconds taken: " + ts.Milliseconds); 
    } 
    Console.ReadLine(); 
    return; 
} 
static async Task<object> GetStuffAsync() 
{ 
    var doubleTask = AsyncMethods.GetdoubleAsync(); 
    var StringTask = AsyncMethods.GetStringAsync(); 
    var DateTimeTask = AsyncMethods.GetDateTimeAsync(); 

    return new 
    { 
     _double = await doubleTask, 
     _String = await StringTask, 
     _DateTime = await DateTimeTask, 
    }; 
} 

因为它可以在每个可见方法我增加了1秒的延迟。下面是输出:

Thread.CurrentThread.ManagedThreadId: 10 
Thread.CurrentThread.ManagedThreadId: 10 
Thread.CurrentThread.ManagedThreadId: 10 
Thread.CurrentThread.ManagedThreadId: 10 
{ _double = 80, _String = async, _DateTime = 2/15/2017 4:32:00 AM } 
Seconds taken: 1 milliseconds taken: 40 

Thread.CurrentThread.ManagedThreadId: 10 
Thread.CurrentThread.ManagedThreadId: 10 
Thread.CurrentThread.ManagedThreadId: 10 
Thread.CurrentThread.ManagedThreadId: 10 
{ _double = 80, _String = async, _DateTime = 2/15/2017 4:32:03 AM } 
Seconds taken: 1 milliseconds taken: 16 

现在我有2个问题:

  1. 为什么一切都发生在单个线程?
  2. 为什么当我等待3秒时延迟只有1秒?
+0

说真的,VS没有警告你同步调用异步代码? – stt106

+0

如果3个任务并行运行,每个任务延迟1秒,则总延迟为1秒。你为什么期望3秒延迟? – juanreyesv

+0

@juanreyesv - 看到它们都是一样的threadids。他们如何在单线程上并行运行? –

他们都在同一个线程开始。当您按顺序调用三个Async方法时,它们都会同步执行,直到第一个调用为止。 (在await后,他们成为国家机器,拿起他们离开时,他们获得计划如果后检查线程ID await Task.Delay呼叫时,您可能会发现,延续运行在不同的线程 - 至少在这里在一个控制台应用程序)

至于为什么它只是拖延1秒...这就是你要告诉它做的。你有三个异步任务,全部同时运行,每个任务延迟一秒钟。你并不是说“等到第一个任务完成之后再开始第二个任务” - 事实上你正在做相反的事情,从三个开始,然后等待所有三个 - 所以他们并行运行。

在调用线程中发生GetdoubleAsync(),GetStringAsync()和GetDateTimeAsync()中的Console.WriteLine()调用,因为它们发生在第一次继续之前。

Your await Task.Delay()调用使线程返回调用代码。

当()的返回Task.Delay任务的完成,这些任务继续返回他们的价值观,并设置其任务完成。

这允许您在GetStuffAsync()的3个等待(以顺序,同步顺序)返回。每个人都必须等待1秒,然后才标记为完成,但他们正在屈服并同时发生。

我想你正在寻找System.Threading.Tasks.Parallel来同时做事情。 Async ... await对于产生线程很有用。

第一关:如果你有两个问题请问两个问题。不要在一个问题中提出两个问题。

为什么一切都发生在单个线程?

这就是错误的问题。正确的问题是:为什么你认为在第二个线程上会发生什么?

在这里,我会给你一个任务:等5分钟,然后检查你的电子邮件。当你在等待时,做一个三明治。 你有没有雇人去做等待或做三明治?很明显不是。线程是工作者。如果工作可以由一名工作人员完成,则不需要雇用一名工人。

整点await避免如果您不需要额外的线程。在这种情况下,你不需要。

为什么当我等待3秒时延迟只有1秒?

比较这两个工作流程。

  • 等5分钟;当你在等待,做一个三明治
  • 然后检查你的电子邮件
  • 然后等待五分钟;当你在等待,做一个三明治
  • 然后检查你的电子邮件
  • 然后等待五分钟;当你在等待,做一个三明治
  • 然后检查你的电子邮件

如果执行工作流程,你会等共十五分钟。

你写的工作流程是:

  • 等待五分钟
  • 同时,等五分钟
  • 同时,等五分钟
  • ,而你在等待,做一个三明治
  • 然后检查您的电子邮件

You onl您需要等待五分钟的工作流程;所有的延误都发生在同一时间。

您是否看到您现在错误地编写了程序?

这里需要了解的关键是等待是程序中的一个点,其中await的延续被延迟到等待完成的任务完成后

如果您没有等待,程序会自动继续而不等待。这就是await的含义。

+0

答案中始终是“有趣”的示例! – stt106

+0

@Eric Lippert:写这个测试程序的关键是看异步与线程的行为。这个问题仍然让我感到震惊,那就是1 + 1 + 1秒的时间片是如何在单线程中完成的?休息一下你对asyncrony的评价是让我尝试这个例子的原因。乔·怀特的回答让我对事情变得更加清楚。 –

+0

哇,再次惊人的答案。关于代码的类比是我读过的最好的。 – VMAtm

您正在同时启动所有任务,因此它们都将并行运行,而不是按顺序运行。这就是为什么一切都在1000毫秒后完成的原因。

此外,async不会创建新线程,它会异步使用当前线程。您可以在异步JavaScript(这是单线程环境)或Unity3D中的协程中看到这种行为。它们都允许没有线程的异步行为。

因此,您的每个任务都在同一个线程上运行,并在1秒内完成。