2017-08-11 73 views
5

我創建了兩種方法可以打印x和y 100次。我希望它們能夠併發運行,並且我希望輸出是xxxxyxyyyxyyxyx ... sthg。 它不打印任何東西。我在這裏錯過了一些邏輯嗎?爲什麼控制檯上沒有打印?

using System; 
using System.Threading.Tasks; 

namespace ConsoleApplication32 
{ 
    internal class Program 
    { 
     public static async Task<int> Print1() 
     { 
      await Task.Run(() => 
      { 
       for (int i = 0; i < 100; i++) 
       { 
        Console.Write("x"); 
       } 
      }); 

      return 1; 
     } 

     public static async Task<int> Print2() 
     { 
      await Task.Run(() => 
      { 
       for (int i = 0; i < 100; i++) 
       { 
        Console.Write("y"); 
       } 
      }); 

      return 1; 
     } 

     public static void Run() 
     { 
      Task<int> i = Print1(); 
      Task<int> k = Print2(); 
     } 

     private static void Main(string[] args) 
     { 
      Run(); 
     } 
    } 
} 
+0

爲什麼不直接附加調試器並查看? – Liam

+0

'Program'在子線程中加載兩個任務並立即結束,結束所有它的子線程,所以'Print'都不會運行。等待任務結束。 –

回答

2

問題是,你沒有等待任務完成,因此程序正在終止(不再有同步代碼阻塞)。

如果你想在任務同時運行,而是執行此操作:

public static void Run() 
{ 
    Task<int> i = Print1(); 
    Task<int> k = Print2(); 
    Task.WaitAll(i, k); 
} 

另一種方式來等待這個任務完成,將做到這一點:

public static async Task Run() 
{ 
    Task<int> i = Print1(); 
    Task<int> k = Print2(); 
    await Task.WhenAll(i, k); 
} 

public static void Main(string[] args) 
{ 
    Run().GetAwaiter().GetResult(); 
} 
+0

我試過你說的,但是這次按順序處理方法。輸出:xxxxxxxx(100次)yyyyyyy(100次)。我需要輸出像xxxxyyxyxyxyxyxyyyx ... – Lyrk

+0

不,他們沒有@Lyrk。他們碰巧按順序運行。你的任務不是很多,這意味着他們會寫出非常快的含義,第一個可能會在第二個被初始化之前完成。他們仍然運行在獨立的線程 – Liam

+3

要支持@Liam所說的話,你可以在'Print1'循環中添加一個'await Task.Delay(10)'# –

5

試過你代碼並且工作得很好。可能你認爲它不起作用,因爲控制檯窗口關閉得非常快。在主添加Console.ReadLine()

private static void Main(string[] args) 
{ 
    Run(); 
    Console.ReadLine(); 
} 
11

說明

有我們需要調整,以獲得所需的輸出有兩件事情:xxxxyxyxyyyxyyxyx

  1. Run方法不等待Task iTask k完成

    • 這意味着控制檯應用程序將在兩個Task氏完全
    • 作爲Andrei Matracaru提到了他的答案之前關閉,如果您添加Console.ReadLine(),它將阻止控制檯應用程序在字符串打印到屏幕之前關閉。
    • 更優雅的解決方案是使用Task.WhenAllTask.WhenAll將允許其調用線程(在這種情況下,主線程)的代碼產量不凍結應用程序的UI,並允許應用程序繼續在屏幕上打印,直到Tasks完成。這對於一個簡單的控制檯應用程序來說可能看起來微不足道,但在構建任何用戶交互式應用程序(如移動應用程序或Web應用程序)時永不鎖定主線程是至關重要的;鎖定主線程是導致應用程序「凍結」的原因。
    • Camilo Terevinto的回答以上建議Task.WaitAll。這也是不好的做法,因爲它也會凍結申請; Task.WaitAll不等待,不能屈服於調用線程。
  2. Print1()Print2()永不屈服主線程

    • 這些方法在後臺線程執行,但不包括for loop
    • 內的awaitawait是必要的在for loop的內部允許CPU繼續執行主線程,因爲Console.Write()只能打印到主線程的屏幕上。

代碼

using System; 
using System.Threading.Tasks; 

namespace ConsoleApplication32 
{ 
    internal class Program 
    { 
     public static async Task<int> Print1() 
     { 
      for (int i = 0; i < 100; i++) 
      { 
       Console.Write("x"); 
       await Task.Delay(10); 
      } 

      return 1; 
     } 

     public static async Task<int> Print2() 
     { 
      for (int i = 0; i < 100; i++) 
      { 
       Console.Write("y"); 
       await Task.Delay(10); 
      } 

      return 1; 
     } 

     public static async Task Run() 
     { 
      var i = Print1(); 
      var k = Print2(); 

      await Task.WhenAll(i, k); 
     } 

     private static void Main(string[] args) 
     { 
      Task.Run(async() => await Run()).GetAwaiter().GetResult(); 
     } 
    } 
} 

輸出

xyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxxyxyxyxy 按任意鍵繼續......

C#7.1替代

C#7.1介紹爲具有async Main方法的能力。這意味着我們可以按如下方式調整Main方法:

... 

    private static async Task Main(string[] args) 
    { 
     await Run(); 
    } 
    ... 
+2

令人驚訝的是,你使用'Task.Delay'得到了一個答案,並得到了更多upvotes ...'Task.Run(async()=>等待Run())。Wait();''是一個雙重包裝 - 你可以做'Run()。Wait()'。你也沒有提到只有Visual Studio 2017 Preview 3可以運行c#7.1代碼 –

+5

而不是使用'.Wait()'或'.Result',我強烈推薦使用'.GetAwaiter()。GetResult()'相反,當某些事件拋出異常時,它會提供更好的堆棧跟蹤。 – Cheesebaron

+2

有關控制檯應用程序和線程關聯的額外獎勵材料:https://blogs.msdn.microsoft。COM/pfxteam/2012/01/20 /等待-的SynchronizationContext和 - 控制檯應用程序/ – Krumelur

相關問題