2015-11-15 38 views
6

比方說,我有一個同步運行的方法fooCPU(它不會調用執行I/O的純異步方法,或通過調用Task.Run或類似的方法使用其他線程來運行其代碼)。該方法執行一些繁重的計算 - 它受CPU限制。UI線程有什麼特別之處?

現在我在我的程序中調用fooCPU而不委託它由工作線程執行。如果一行fooCPU將花費很長時間運行,那麼在完成之前不會執行其他行。例如,從UI線程調用它會導致UI線程凍結(GUI將變爲無響應)。

當我說async/await是一個模仿多線程。兩條不同代碼的代碼在單個線程中輪流執行。如果其中一條線路需要很長時間才能運行,則不會執行其他線路,直至完成。,

我已經被告知在UI線程中使用async是正確的,但對於所有其他情況(ASP.NET,線程池上的異步,控制檯應用程序等)並非如此。

誰能告訴我這可能意味着什麼? UI線程與控制檯程序的主線程有何不同?

我認爲沒有人希望這個論壇上的任何人繼續討論相關主題,因爲它們出現在評論中,所以最好提出一個新問題。

+0

我會在win窗體應用程序和控制檯應用程序上編譯相同的代碼,並使用ildasm或反射器檢查編譯的IL的差異以查看我自己。 –

+1

@OguzOzgul這對許多問題很有用,但不是這個 - 代碼完全一樣。全局狀態有哪些變化 - 同步上下文的存在。 – Luaan

+1

答案相當不錯,但唯一能滿足你好奇心的就是明白等待的細節。搜索「.net等待內部」看起來很不錯。如果你有一個小時的時間,這將會回答一切,我希望。 – usr

回答

10

我建議你閱讀我的async intro post;它解釋了asyncawait關鍵字是如何工作的。然後,如果您有興趣編寫異步代碼,請繼續使用我的async best practices article

開場後的相關部分:

異步方法的開頭就像任何其他方法執行。也就是說,它同步運行,直到它遇到「等待」(或拋出異常)。

所以這就是爲什麼inner method in your console code example(沒有await)同步運行。

等待檢查,等待看它是否已經完成;如果awaitable已經完成,那麼方法會繼續運行(同步,就像常規方法一樣)。

因此,這就是爲什麼outer method in your console code example(這是await荷蘭國際集團內方法這是同步的)被同步運行。

稍後,等待完成時,它將執行異步方法的其餘部分。如果您正在等待內置的等待時間(例如任務),則異步方法的其餘部分將在「await」返回之前捕獲的「上下文」上執行。

此「上下文」是SynchronizationContext.Current除非是null,在這種情況下,它是TaskScheduler.Current。或者,更簡單的版本:

究竟是什麼「上下文」? 答案很簡單:

  1. 如果你在一個UI線程,那麼它是一個UI背景。
  2. 如果您對ASP.NET請求做出響應,那麼這是一個ASP.NET請求上下文。
  3. 否則,它通常是一個線程池上下文。

把所有這一切放在一起,你可以想像async/await像這樣的工作:方法被分爲若干「塊」,每個await充當地步方法是分裂的。第一個塊始終同步運行,並且在每個分割點可以同步或異步繼續。如果它異步繼續,那麼它將繼續在捕獲的上下文中(默認情況下)。 UI線程提供將執行UI線程上下一個塊的上下文。

因此,要回答這個問題,有關UI線程的特殊之處在於它們提供了一個SynchronizationContext,它將工作排隊到同一個UI線程。

我認爲沒有人希望這個論壇上的任何人繼續討論相關主題,因爲它們出現在評論中,因此最好提出一個新問題。

那麼,堆棧溢出具體是不是打算成爲一個論壇;這是一個問題&答案網站。所以它不是要求詳盡教程的地方;當你試圖讓代碼工作,或者在研究了你所能做的所有事情之後,你不明白某些東西時,這是一個地方。這就是爲什麼對SO的評論(有目的地)受到限制 - 它們必須簡短,沒有很好的代碼格式等。本網站的評論意在澄清,而不是討論或論壇帖子。

+0

謝謝。我實際上已經閱讀了一些msdn博客和你的博客文章。 'UI線程提供了一個將在UI線程上執行下一個塊的上下文' - 它不遵循你的介紹文章中關於異步/等待的內容,是不是?我認爲上下文不是線程的標識符(許多線程可以共享一個上下文)。關於「這個背景」究竟是什麼?簡單的回答:如果你在一個UI線程上,那麼這是一個UI上下文。' - 它不能解釋_context_的真實含義。 – user4205580

+0

所以我猜'UI線程提供了一個上下文,它將執行UI線程中的下一個塊'來自[this](https://msdn.microsoft.com/en-us/magazine/gg598924.aspx),引用: _當前實現爲每個UI線程創建一個WindowsFormsSynchronizationContext。 – user4205580

+0

@ user4205580:在介紹文章中,在「簡單」解釋之後的下一段中:'如果SynchronizationContext.Current不爲null,則它是當前的SynchronizationContext。 (UI和ASP.NET請求上下文是SynchronizationContext上下文)。' –

3

鑑於

async void Foo() { 
    Bar(); 
    await Task.Yield(); 
    Baz(); 
} 

你是對的,如果Foo()被稱爲UI線程上,然後Bar()立即調用,並Baz()被稱爲在一段時間後,卻依然在UI線程上。

但是,這不是線程本身的屬性。

什麼實際發生的是,這種方法被分成類似的東西來

Task Foo() { 
    Bar(); 
    return Task.Yield().Continue(() => { 
    Baz(); 
    }); 
} 

這實際上不是正確的,但以何種方式它是錯的並不重要。

傳遞給我的假設Continue方法的參數是可以通過任務確定的某種方式調用的代碼。該任務可以決定立即執行,也可以決定在以後的某個點在同一個線程來執行它,或者它可以決定在以後的某個點上不同的線程來執行它。

其實,任務本身並不決定,他們只是將代理傳遞給SynchronizationContext。這是決定如何處理要被執行的代碼執行此同步上下文。

而這有什麼的線程類型之間的不同:一旦你從一個線程訪問任何的WinForms控制,那麼的WinForms安裝該特定線程的同步環境,這將在以後的某個點安排要被執行的代碼在同一個線程上。

ASP.NET,後臺線程,它的所有不同的同步環境,這是什麼造成的代碼如何被安排的變化。

9

這很簡單,線程一次只能做一件事。因此,如果您將UI線程發送到與UI無關的樹林中,比如說一個dbase查詢,那麼所有的UI活動都會停止。沒有更多的屏幕更新,沒有響應鼠標點擊和按鍵。它看起來和行動冷凍

你可能會說,「好吧,我只是用另一個線程來做UI」。在控制檯模式下工作,種類。但不是在GUI應用程序中,使代碼線程安全是困難的,並且UI根本不是線程安全的,因爲涉及的代碼太多了。不是你寫的那種,你用類似花哨的類庫包裝器的類型。

通用的解決方案是反轉,做一個工作線程上的非UI相關的東西,並離開UI線程只處理簡單的用戶界面的東西。異步/等待可以幫助你做到這一點,什麼是在等待工人的權利運行。唯一的辦法就是搞砸了,而且這種情況並不少見的是要求UI線程仍然做了太多的工作。就像每毫秒向文本框添加一行文本一樣。這只是糟糕的UI設計,人類不會那麼快讀。

+0

'在工作者上等待運行的權利' - 我想我已經閱讀了很多答案(這裏也是如此),指出「await」本身不會創建或切換線程。通過'await syncFoo()'調用一個同步方法不會讓它在另一個線程上執行。實際上,'syncFoo()'和'await syncFoo()'沒有區別。 – user4205580

+0

當然,事實並非如此。但是他的正確思想中沒有人會把任何東西放在不同步的右邊。等待肯定會幫助任何人獲得正確的思想,而不是一切都在那裏。 –

+0

對不起,也許我錯了,但我認爲即使方法是異步的,異步本身也不會讓它在工作線程上執行 - 這取決於該方法是否調用例如「Task.Run」。純異步方法通常不使用其他線程,它們在[比線程更低的層次上執行I/O操作(http://blog.stephencleary.com/2013/11/there-is-no-thread.html) 。 – user4205580

相關問題