3

在ASP.NET MVC中我有一個Action接受用戶輸入的電子郵件地址列表,驗證它們,然後將HTML和文本版本的電子郵件發送給每個收件人。用戶然後被重定向到感謝頁面。因爲沒有必要等待重定向到感謝頁面,直到發送所有電子郵件消息,我使用Task.Factory.StartNew方法來啓動一個實際發送電子郵件的新任務,並且工作正常;用戶立即被重定向到感謝頁面,並且電子郵件消息在單獨的線程上發送。 因此,一切都按我的想法工作,但我仍然有關於多線程的以下問題。我在最後幾天閱讀了很多帖子,但我仍然沒有全部答案,下面是我從這些帖子中提取的一些事實(請注意,這些僅僅是我的假設,我在此寫下它們,以便您可以發表評論並提高它們):使用任務庫的多線程

  1. 任務庫使用線程的線程池

    如果您正在使用任務庫,您通過使用線程ASP.NET線程池創建新的線程。這意味着少一個線程可用於爲應用程序提供其他ASP.NET請求。因此,通過使用任務庫,您不會通過將任務卸載到某些其他操作系統線程來優化ASP.NET線程的使用,只有用戶體驗更好,但任務庫使用另一個可用於提供其他ASP.NET請求的線程。所以唯一的結果是用戶不需要等待。

  2. 手冊線程

    如果你真的想使用操作系統線程,你必須明確地開始新的主題。但即使您啓動新的操作系統線程,您也需要擁有多個內核或處理器的機器才能真正看到應用程序可擴展性的提高。

  3. 後臺線程池

    一些帖子談論ASP.NET線程池和一個單獨的應用程序的後臺線程池,其用於後臺任務。換句話說,每個ASP.NET應用程序都有一個線程池來提供應用程序請求,另一個線程池提供後臺任務。我不認爲這是真的,我認爲每個ASP.NET應用程序只有一個線程池,並且來自該池的線程用於服務於應用程序請求和後臺任務。當通常只有一個線程正在運行(UI線程)並且您必須顯式啓動新線程時,也許可以這麼說。但是ASP.NET在其基礎上是多線程的。

這裏有幾個問題:

  1. 我已閱讀有關恐龍埃斯波西託在編程ASP.NET MVC 2異步MVC控制器。他寫的異步控制器如何使用OS線程長期運行的任務,因此ASP.NET線程擔任ASP.NET請求最初是現在可以自由地服務器的其他請求。

    雖然我並不需要異步控制器在這裏,我的問題是我怎麼能在我的例子中使用這樣的OS線程。我是否必須明確啓動單獨的線程,或者這對於任務庫來說也是可能的?

  2. 即使我卸載等TAKS一些操作系統的線程會不會有任何好處,如果機器只有一個處理器?我認爲,要真正提高應用程序的可擴展性的多核機器是必要的。

回答

1

[...]他寫了關於異步控制器如何使用操作系統線程進行長時間運行的任務,因此最初服務於ASP.NET請求的ASP.NET線程現在可以免費服務於其他請求。

嗯,這取決於你有在異步控制器什麼代碼。它可能在OS Thread上執行一些代碼,但它不必。通常會發生的是,您啓動了一些異步操作,在運行時不使用任何線程。並且當它結束時,結果在一些ThreadPool線程上執行。

儘管我不需要異步控制器,但我的問題是如何在我的示例中使用這種操作系統線程。我是否必須明確啓動單獨的線程,或者這對於任務庫來說也是可能的?

您可以使用​​開始一個新線程。這不保證開始一個新的線程,但實際上它確實。

即使我卸載這些taks到某些操作系統線程,如果機器只有一個處理器,會有什麼好處嗎?我認爲,要真正提高應用程序的可擴展性的多核機器是必要的。

如果該線程同步執行某些IO綁定操作可能會有好處。例如,發送電子郵件所花費的大部分時間很可能不會花費CPU使用,而是等待服務器響應。如果是這種情況,使用另一個線程(無論是單獨的線程還是來自ThreadPool的線程)都可能是有益的。當然,如果你異步地執行這個操作會更好,它不會阻塞任何線程,但是這可能會讓你的代碼變得更加複雜(並且有時甚至不可能)。你寫

至於其他的事情:

如果您正在使用任務庫,您通過使用線程ASP.NET線程池創建新的線程。這意味着,一個線程較少的可用於服務於其他ASP.NET請求的應用

是的,在正常情況下(如果使用默認TaskScheduler並且不指定​​)。但是,如果遇到問題,更好的解決方案可能是增加ThreadPool限制,而不是自行創建Thread以執行短期運行任務。那是因爲創建和銷燬線程是一項昂貴的操作,如果性能對您來說很重要,您應該避免這種操作。

這個ASP.NET應用程序有一個線程池來提供應用程序請求,另一個線程池提供後臺任務。我不認爲這是真的,我認爲每個ASP.NET應用程序只有一個線程池,並且來自該池的線程用於服務於應用程序請求和後臺任務。

它比這稍微複雜一些,但沒錯,ASP.NET請求和線程池的任務(開始使用ThreadPool.QueueUserWorkItem()或啓動使用默認選項Task)是在線程的同一池中運行。

+0

svick,謝謝。總而言之,任務默認使用線程池中的線程。如果指定TaskCreationOptions.LongRunning,則可能會使用新的非線程池線程。 對於短期運行任務,最好增加線程池中的線程數量,而不是創建非線程池線程。 非線程池線程只能用於長時間運行的任務,這些任務可能會通過使用線程池線程傷害性能。 – Joxi 2012-04-10 07:40:53

4

你真正的問題是,ASP.NET可以隨時回收你的AppDomain。當發生這種情況時,你的後臺線程將被極端的偏見中止。不管你使用線程池線程還是啓動你自己的線程 - 如果ASP.NET不能「知道」線程,它可以並且會銷燬AppDomain,並把它帶入你的線程。

「正確」的解決方案是有一個單獨的Windows服務進程是一個WCF服務器。然後,您的網絡應用程序就可以向服務發送命令,並執行它們 - 例如發送一些電子郵件。

快速和骯髒的方法是在您的web應用程序發送電子郵件隱藏的行動。啓動對該Action的異步請求,然後返回「謝謝」頁面而不等待結果。 ASP.NET不關心請求來自你的應用程序 - 它只是將它視爲另一個請求。由於ASP.NET「知道」請求,它不會中止它。