2016-03-06 183 views
1

我想了解TPL。不幸的是,我無法用返回類型來克服任務。從我讀過的內容來看,我認爲將任務分配給變量會異步啓動。當您需要返回值時,您只需等待它,這可確保當前線程等待Task<T>完成。在MSDN爲什麼任務<TResult>在等待結果前等待結果?

例子:

// Call and await in separate statements. 
Task<int> integerTask = TaskOfT_MethodAsync(); 

// You can do other work that does not rely on integerTask before awaiting. 
textBox1.Text += String.Format("Application can continue working while the Task<T> runs. . . . \r\n"); 

int result2 = await integerTask; 

我的理解:第一條語句應該啓動任務後,馬上文本框被追加。然後線程被阻塞,直到integerTask完成。

然而,當我試圖對我自己,它沒有工作方式:

static void Main() 
{ 
    var task = new Task(RunItAsync); 
    task.Start(); 
    task.Wait(); 
} 
static async void RunItAsync() 
{ 
    // Should start the task, but should not block 
    var task = GetIntAsync(); 
    Console.WriteLine("I'm writing something while the task is running..."); 
    // Should wait for the running task to complete and then output the result 
    Console.WriteLine(await task); 
} 
static Random r = new Random(); 
static async Task<int> GetIntAsync() 
{ 
    return await Task.FromResult(GetIntSync()); 
} 
public static int GetIntSync() 
{ 
    // Some long operation to hold the task running 
    var count = 0; 
    for (var i = 0; i < 1000000000; i++) { 
     if (i % 2 == 0) count++; 
    } 
    return r.Next(count); 
} 

沒有輸出,幾秒鐘後它輸出眼前的一幕:

I'm writing something while the task is running... 
143831542 

我做錯了什麼?

+1

我認爲你需要退後一步,更深入瞭解,使異步工作所需的所有概念。 Stephen Cleary的一系列文章是對這個主題的一個很好的介紹。 http://blog.stephencleary.com/2012/02/async-and-await.html –

+0

是的,很遺憾,我錯誤地瞭解了我所學到的大部分內容。 – Sorashi

+0

這是棘手的東西。如果你想要更多的技術性的東西,雖然這個東西有點過時,但是當我們設計它的時候,我寫了一系列文章。 https://blogs.msdn.microsoft.com/ericlippert/tag/async/。從底部開始,這些列表從大多數到最近都列出。 –

回答

10

ROM我讀過,我認爲賦值給一個變量任務異步啓動它。

這是完全錯誤的。

在這裏,我有一個任務:做一個三明治。我把它寫在一張紙上,然後放在一個標有「任務」的抽屜裏。我開始做三明治了嗎?號碼

我開始製作一個三明治。開始製作三明治是否會在我的抽屜中出現一張描述任務的紙張?號碼

變量和任務完全沒有關係。不管是什麼原因讓你相信他們之間有什麼關係,現在不要再相信了。

當你需要返回值,你只是等待它

是。

確保當前線程等待任務完成。

不,如果通過「等待」您的意思是「塊」。

我的理解:第一條語句應該啓動任務

當然,假設TaskOfTMethodAsync啓動任務並返回啓動任務,這似乎是合理的。

立即後的文本框中附加

是。

然後線程被阻塞,直到integerTask完成。

完全不是。等待的關鍵是不要阻止線程!如果你給我一項任務 - 做一個三明治 - 並且你等着我完成這個任務,那麼當我讓你三明治的時候,你不要小憩了!你不斷完成工作!否則,僱用我來讓你成爲三明治有什麼意義?你想僱用工人爲你做工作,而你做別的事情,而不是當你睡覺等待他們完成。

我在做什麼錯?

兩件事情。首先,你將一個無效的方法變成一項任務,這是非常錯誤的。異步方法不應該是無效的,它應該返回一個任務!任務代表異步工作。其次,你只是返回完成的任務!

讓我們一行一行地說一遍,說出它的作用。

var task = new Task(RunItAsync); 

創建一個任務來表示該操作。

task.Start(); 

在線程池中啓動任務。

然後主線程阻塞任務的完成。

好吧,這種方法是在線程池運行:

static async void RunItAsync() 
{ 

這是什麼電話呢?

var task = GetIntAsync(); 

好吧,讓我們來看看:

static async Task<int> GetIntAsync() 
{ 
    return await Task.FromResult(GetIntSync()); 
} 

第一件事情就是打電話GetIntSync。這會持續很長時間。然後它返回一個值。然後我們產生一個代表該值的完成的任務。然後我們等待那個任務。等待已完成的任務會產生價值。所以這與

value1 = GetIntSync() 
    task1 = completed task representing value1 
    value2 = value of task1 
    task2 = completed task representing value2  
    return task2 

這裏完全沒有異步。這是編寫「return GetIntSync()」的非常非常複雜的方法 - 運行該方法,然後構造不是一個,而是兩個完成的任務,代表已完成的任務!

再次說明:此方法返回一個任務。這個方法的所有子任務已經完成了,所以我們只需返回一個完成的任務。

該任務會發生什麼?請記住,我們在:

var task = GetIntAsync(); 

這樣,任務就完成了。 (這是「任務2」。)

Console.WriteLine("I'm writing something while the task is running..."); 

不,你正在寫任務後完成任務。你返回了一個完成的任務!

// Should wait for the running task to complete and then output the result 
Console.WriteLine(await task); 

不,任務已經完成。這會提取值而不等待並打印它。沒有什麼可以等待的;任務已經完成!

現在從這個方法返回什麼?沒有。它是一個無效的方法。所以我們現在回來了,完成了方法的所有工作。

接下來會發生什麼?傳遞給原始任務的委託已在工作線程上完成運行,因此在工作線程上運行的任務完成並向主線程發出可以停止等待的信號。

0

在你的代碼有

public static int GetIntSync() 

Getintsync有隻是一個普通的無聊常規,你在寫第一行之前調用它,所以它做的所有工作,然後說,即時通訊做別的事情。

的例子工作,因爲調用的函數是一個異步功能,所以它的建立與

Task<T> something = myAsyncFunc(params) 
Now output im going to do something 
T result = await myAsyncFunc.. 

現在導致有結果做其他事情。

這就像建立一個deligate

第一行基本上是deligate,所以犯規動作代碼。在等待的時候,你的代碼在打我。

microsofts example

我登記,所以不是最好的地方,但我希望能看到你的錯誤,以及如何解決它

1

有幾個問題。

  1. 你在這裏沒有開始任何異步。 async-await不會自動使事物異步或創建任何線程。閱讀stephen's introductory article on the subject

    要卸載一些工作,您需要使用Task.RunTask.Factory.StartNew

  2. 您正在使用async void方法;沒有簡單的方法來等待它的完成。您應該使用async TaskRead more

所以,你的代碼就變成了:

static Task<int> GetIntAsync() 
{ 
    return Task.Run(()=> GetIntSync()); 
} 

static async Task RunItAsync() 
{ 
    // Should start the task, but should not block 
    var task = GetIntAsync(); 
    Console.WriteLine("I'm writing something while the task is running..."); 
    // Should wait for the running task to complete and then output the result 
    Console.WriteLine(await task); 
} 

而且你不需要使用task.Start();手動啓動該任務; Task.Run已經交給你一個開始的任務。

鑑於兩個改變你的代碼應該按預期工作

1

我在這裏看到兩個大問題: 首先是你使用Task.FromResult(result):它只不過是包裝結果,所以它看起來像一個任務,所以你可以使用任務的方法。當你調用return await Task.FromResult(GetIntSync());,它:

  1. 計算GetIntSync完全
  2. 返回int
  3. 把它傳遞給FromResult
  4. 立即與成品Task<int>返回,
  5. 呼叫await對完成任務,所以它馬上繼續,
  6. 並返回Task<int>

GetIntAsync不同步了!第二大問題是你正在混合測試你的異步/等待技能(也使用Task),並行執行使用Task.Run。異步執行意味着線程的執行順序不一定與代碼順序同步。儘可能快地執行異步方法中的代碼。異步方法可以等待,控制權返回給調用者,直到它表示它已準備好再次工作。這樣做的目的是保持線程以及CPU忙碌。爲了說明,我們使用我最喜歡的機制:metaphores!

  • 讓邏輯處理器成爲桌面工作者。每次切片時,您都會走到一個桌子(線程)上,並在其上面放置一個文件夾(方法),一個接一個地處理其中的表格(線條)。如果一行是一種方法,您可以抓住該文件夾,處理裏面的表格,然後返回到您正在處理的文件夾。

  • 然後一張表格X需要你老闆的簽名。你把它交給你的老闆:從Worker.WorkDay()調用異步方法Task task = GetBossToSign(Form X)。然後你在其他表格上工作。當你拿回X簽名時,你完成了X,然後繼續你的位置,而不是浪費一秒鐘。

  • 但是現在您來到表單Y,要求X已完成。幸運的是,Worker.WorkDay()被標記爲async,因此您可以撥打await task來表示:在此文件夾下工作,直到您看到X還給您,仍在處理此過程。只有標記爲async的文件夾可以通過await安全地收起。另一方面,如果您打電話給task.Wait(),那麼您將該桌面標記爲禁區(阻塞線程),直到任務完成。如果在使用底層文件夾時使用await,也會發生這種情況。工作人員(處理器)將嘗試找到另一個工作臺(準備好的線程),這可能與您試圖精簡的實際進程無關。

  • 如果你打電話給Task.Run(RunItAsync)(或者創建一個任務,然後開始它),你會去一些空的桌子,並在那裏留下一個文件夾RunItAsync。你將在自己的桌子上工作。最理想的做法是快兩倍。如果你是唯一的工作人員(處理器),那麼它什麼也不做,因爲你只是要在兩個辦公桌上分配工作時間片。

  • 所以,現在你的代碼做了什麼:一個線程工作在辦公桌A,其中Main是文件夾。它抓住文件夾RunItAsync,將其放在空桌子B上,然後將此桌面標記爲不可用。 B(可能是也可能不是同一個工作人員)的工作人員呼叫GetIntAsync,然後GetIntSync。他自己填寫表格,將完成的文件夾放在桌面C上,使用Task.FromResult,然後檢查文件夾是否在桌面上。C完成await。它是!所以await什麼都不做,GetIntAsync返回。完成Task分配給任務,B打印第一行,檢查並看到task已完成,並打印第二行。文件夾RunItAsync已完成,因此辦公桌A被標記爲可用,辦公桌B被放棄。一名工人(也可能是也可能不是同一名工人)前往辦公桌A並執行task.Wait()之後的任何事情。

    • 因此,程序中沒有兩個工作人員同時工作,也沒有任何執行與寫入的代碼異步執行。

要測試你的非同步/等待技能,只需使用一個非同步方法已經可用,例如WebClientDownloadStringTaskAsync("http://stackoverflow.com")。例如:

//Calling asynch method, running on the same thread, 
//doing the work as it is eligible for doing.; 
Task<string> x = new WebClient().DownloadStringTaskAsync("http://stackoverflow.com"); 
Console.WriteLine("I'm writing something while the task is running..."); 
Console.WriteLine("Content of webpage is: {0}", await x); 

此執行的線程傳遞控制到當前方法的調用者,直到下載完成後,通過使用await x

爲了測試你的任務並行技能,任何同步工作都可以。

//Calling Task to delegate work to be done in parallel, 
//running(hopefully) on another thread. 
Task<int> longTask = Task.Run(() => {Thread.Sleep(1000); //this is hard work 
            return 1;}); 
Console.WriteLine("I'm writing something while the task is running..."); 
Console.WriteLine("The number one is: {0}", await longTask);