2017-02-14 158 views
0

這是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。他們如何在單線程上並行運行? –

回答

1

他們都在同一個線程開始。當您按順序調用三個Async方法時,它們都會同步執行,直到第一個調用爲止。 (在await後,他們成爲國家機器,拿起他們離開時,他們獲得計劃如果後檢查線程ID await Task.Delay呼叫時,您可能會發現,延續運行在不同的線程 - 至少在這裏在一個控制檯應用程序)

至於爲什麼它只是拖延1秒...這就是你要告訴它做的。你有三個異步任務,全部同時運行,每個任務延遲一秒鐘。你並不是說「等到第一個任務完成之後再開始第二個任務」 - 事實上你正在做相反的事情,從三個開始,然後等待所有三個 - 所以他們並行運行。

0

在調用線程中發生GetdoubleAsync(),GetStringAsync()和GetDateTimeAsync()中的Console.WriteLine()調用,因爲它們發生在第一次繼續之前。

Your await Task.Delay()調用使線程返回調用代碼。

當()的返回Task.Delay任務的完成,這些任務繼續返回他們的價值觀,並設置其任務完成。

這允許您在GetStuffAsync()的3個等待(以順序,同步順序)返回。每個人都必須等待1秒,然後才標記爲完成,但他們正在屈服並同時發生。

我想你正在尋找System.Threading.Tasks.Parallel來同時做事情。 Async ... await對於產生線程很有用。

12

第一關:如果你有兩個問題請問兩個問題。不要在一個問題中提出兩個問題。

爲什麼一切都發生在單個線程?

這就是錯誤的問題。正確的問題是:爲什麼你認爲在第二個線程上會發生什麼?

在這裏,我會給你一個任務:等5分鐘,然後檢查你的電子郵件。當你在等待時,做一個三明治。 你有沒有僱人去做等待或做三明治?很明顯不是。線程是工作者。如果工作可以由一名工作人員完成,則不需要僱用一名工人。

整點await避免如果您不需要額外的線程。在這種情況下,你不需要。

爲什麼當我等待3秒時延遲只有1秒?

比較這兩個工作流程。

  • 等5分鐘;當你在等待,做一個三明治
  • 然後檢查你的電子郵件
  • 然後等待五分鐘;當你在等待,做一個三明治
  • 然後檢查你的電子郵件
  • 然後等待五分鐘;當你在等待,做一個三明治
  • 然後檢查你的電子郵件

如果執行工作流程,你會等共十五分鐘。

你寫的工作流程是:

  • 等待五分鐘
  • 同時,等五分鐘
  • 同時,等五分鐘
  • ,而你在等待,做一個三明治
  • 然後檢查您的電子郵件

You onl您需要等待五分鐘的工作流程;所有的延誤都發生在同一時間。

您是否看到您現在錯誤地編寫了程序?

這裏需要了解的關鍵是等待是程序中的一個點,其中await的延續被延遲到等待完成的任務完成後

如果您沒有等待,程序會自動繼續而不等待。這就是await的含義。

+0

答案中始終是「有趣」的示例! – stt106

+0

@Eric Lippert:寫這個測試程序的關鍵是看異步與線程的行爲。這個問題仍然讓我感到震驚,那就是1 + 1 + 1秒的時間片是如何在單線程中完成的?休息一下你對asyncrony的評價是讓我嘗試這個例子的原因。喬·懷特的回答讓我對事情變得更加清楚。 –

+0

哇,再次驚人的答案。關於代碼的類比是我讀過的最好的。 – VMAtm

0

您正在同時啓動所有任務,因此它們都將並行運行,而不是按順序運行。這就是爲什麼一切都在1000毫秒後完成的原因。

此外,async不會創建新線程,它會異步使用當前線程。您可以在異步JavaScript(這是單線程環境)或Unity3D中的協程中看到這種行爲。它們都允許沒有線程的異步行爲。

因此,您的每個任務都在同一個線程上運行,並在1秒內完成。