2011-12-08 108 views
6

以下代碼,我儘量不工作每一個變化異步方法 - 無論DoSomething() : void和書面叫,或DoSomething() : Task,被稱爲與TaskEx.RunEx(),一些嘗試涉及.GetAwaiter().GetResult()。看到的錯誤包括:"Start may not be called on a task with null action""RunSynchronously may not be called on a task unbound to a delegate""The task has not yet completed"調用從非異步方法

class Program 
{ 
    static void Main(string[] args) // Starting from a non-async method 
    { 
     DoSomething(); 

     Console.WriteLine("Press any key to quit."); 
     Console.ReadKey(); 
    } 

    static async void DoSomething() 
    { 
     Console.WriteLine("Starting DoSomething ..."); 

     var x = await PrepareAwaitable(1); 

     Console.WriteLine("::" + x); 

     var y = await PrepareAwaitable(2); 

     Console.WriteLine("::" + y); 
    } 

    static Task<string> PrepareAwaitable(int id) 
    { 
     return new Task<string>(() => 
     { 
      return "Howdy " + id.ToString(); 
     }); 
    } 
} 

輸出:

開始DoSomething的...

按任意鍵退出。

PrepareAwaitableTaskAction將在更晚複雜。當此動作完成時,無論多長時間,我都希望Task(或其他框架機制)通過將xD分配"Howdy ...",然後再分配給y來恢復。我真正想要做的是攔截等候的物體,處理它們,並在稍後我控制的時候,繼續執行結果(xy)。但是,我在這一大步中並沒有走得太遠,所以我試圖從更小的一步開始。

+1

你在做什麼?當我們不知道「工作」是什麼樣子時,很難知道「不工作」是什麼意思。 –

+0

我希望它能夠完成。由於我的理解有多麼錯誤,對我而言,這似乎很明顯應該做什麼。 –

+0

@uosef:運行什麼?這裏沒有任何異步嗎? –

回答

5

您返回的任務尚未開始(即,它們是「冷」任務);嘗試用以下代碼替換PrepareAwaitable代碼:

static Task<string> PrepareAwaitable(int x) 
{ 
    return Task.Factory.StartNew<string>(() => 
    { 
     return "Howdy " + x.ToString(); 
    }); 
} 
+1

嗯。這樣可行。一個冷的任務怎麼能在以後開始?當我嘗試在原始代碼上啓動()(如果DoSomething():Task),我得到錯誤「啓動可能不會在具有空操作的任務上調用」。 –

5

真的不清楚你想要實現什麼,因爲沒有任何異步發生。例如,這將編譯和運行,但我不知道它是否是你想要什麼:

using System; 
using System.Threading.Tasks; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     DoSomething(); 

     Console.WriteLine("Press any key to quit."); 
     Console.ReadKey(); 
    } 

    static async void DoSomething() 
    { 
     Console.WriteLine("Starting DoSomething ..."); 

     var x = await PrepareAwaitable(1); 

     Console.WriteLine("::" + x); 

     var y = await PrepareAwaitable(2); 

     Console.WriteLine("::" + y); 
    } 

    static async Task<string> PrepareAwaitable(int x) 
    { 
     return "Howdy " + x; 
    } 
} 

注意,這給出了PrepareAwaitable一個警告,因爲沒有什麼異步它;沒有「等待」表達式。整個程序同步執行。 PrepareAwaitable的另一種替代實施方式:

static Task<string> PrepareAwaitable(int x) 
{ 
    return TaskEx.Run(() => "Howdy " + x); 
} 

這更像你以後的樣子嗎?

+1

謝謝! TaskEx.Run()是我期望的路線。這比CarlosFigueira的'Task.Factory.StartNew ()'更好嗎? 「更好」意味着與異步設計者的預期用法一致。哪種方法具有較少的限制性後果,還是它們是同義詞? –

+1

@uosɐs:我相信'TaskEx.Run'大多隻是一個簡寫。可能會有一些細微的差異,但如果有的話,我不會不知情。 –

+0

感謝您的詳細解答!如果可以的話,我會把分數分開。 –

10

首先,閱讀基於任務的異步模式文檔。它在My Documents\Microsoft Visual Studio Async CTP\Documentation之下。本文檔介紹如何設計await自然消耗的API。其次,要認識到Task類和相關API在幾個方面不再適用於新的異步世界。 Task最初是作爲TPL的核心撰寫的。事實證明它非常適合異步編程,所以微軟使用它來代替創建一個新的「Promise」類。但是許多方法都是持久性的,被TPL使用但異步編程不需要。

要回答這個問題,混合同步代碼和異步代碼對於當前的異步CTP並不是非常簡單。我寫了a library,其中包含一個Task.WaitAndUnwrapException擴展方法,可以很容易地從同步代碼調用異步代碼。您也可能對我的AsyncContext class感興趣,這會讓「按任意鍵」提示不必要。

+0

這是一個非常好的答案。您可以更深入地瞭解我在學習異步時的一般性掙扎,如果可以的話,我會分解這些問題。 –