2011-03-13 58 views
28

由於我期望開發高性能,可擴展的TCP服務器,因此我一直在閱讀很多關於線程的內容,這些服務器能夠處理高達10,000-20,000個客戶端,其每個客戶端通過基於命令的系統雙向地與服務器進行雙向通信。服務器將收到一個命令,並按照該命令執行單個(或多個)任務。我的問題是如何在各種情況下適當地使用.NET線程構造,根據正在執行的工作執行可能需要一分鐘到幾個小時的任務。C# - 何時在高活動服務器中使用標準線程,ThreadPool和TPL

最令我困惑的是,在我閱讀的每一個地方,我都看到類似於「使用手動創建的線程(或自定義線程池)來處理'長時間運行'的任務,並將TPL用於短期任務,或者需要並行處理的任務。「究竟是什麼長時間運行的任務?那是5秒60秒嗎?

用什麼時間框架,我應該使用每個創建線程的這三種方法:

  • 手動創建的線程
  • 的.NET ThreadPool類
  • TPL

另一個問題我預期如下 - 說我的服務器確實有20,000個客戶端連接,每個客戶端每秒發送一個命令(可以轉換爲一個或多個任務)。即使使用強大的硬件,我是不是有可能將工作負載的過高壓入任何線程池/工作項目隊列中,從而在隊列緩慢填充到最大值後最終生成OutOfMemoryException?

任何瞭解將不勝感激。

+0

平均每個命令需要處理多長時間? – rene 2011-03-13 09:44:57

+0

就是這樣 - 事實上並沒有說明命令需要多長時間才能完成。我覺得我已經收到足夠的信息來做出相應的規劃。當然,20,000個客戶是一個遠投(5000個更像是它),但我希望在必要時能夠在未來擴展。感謝大家的迴應。 – slashp 2011-03-13 09:57:56

+2

@slashp長時間運行意味着「超過幾百毫秒」,即。破壞你的其他工作的東西(並且引入了顯着的處理延遲)。例如,如果您的目標延遲時間爲5毫秒,那麼即使1毫秒也可以被認爲是「長時間運行」:D另外,我想指出20k個TCP客戶端正在接近實際的限制 - 每個TCP連接需要一個單獨的端口,你最多隻有65535 *最大值 - 他們在關閉後約4分鐘。您可能不得不考慮擴展(更多服務器),而不是擴展(每個服務器更多的連接)。 – Luaan 2014-03-27 08:42:20

回答

17

其實,對於那個場景所有的都是次要的;你應該看的第一件事是asyc-IO,又名.BeginRead(...)等;這允許您通過等待IO完成端口來最小化線程數 - 效率更高。

一旦你有一個完整的消息,在那個尺度我會把這個消息放到一個自定義線程池/同步隊列中。我將有一個受控的號碼線程(不是線程線程或IOCP)爲該隊列處理每個項目提供服務。

碰巧我現在正在做類似的事情(較低的比例)爲了防止內存爆炸,我已經封頂了工作隊列;如果它已滿(即工作人員無法跟上),那麼可能會阻止IOCP一段時間,或許最終超時會告知客戶在IOCP層「太忙」。

+0

+1「隊列+服務線程」 - 很有可能是最好的方法 – 2011-03-13 09:47:44

+1

對不起,我忘了提及我瞭解IOCP將用於網絡部分,我只想給我一些背景知道我想完成什麼。我也明白在使用IOCP時,我需要小心堆碎片,這也會導致OutOfMemoryException。我真的需要澄清何時使用三種結構中的哪一種,以及「長期運行」任務的定義。 – slashp 2011-03-13 09:48:27

+0

@slashp我編輯添加一些關於內存問題的想法;因爲你可能會不斷運行,所以你可以自己擁有這些線程 - 避免使用ThreadPool,並且可以將它們命名爲 – 2011-03-13 09:50:19

10

什麼困惑我最多的是 事實,我到處看,我看到 像「使用​​手動創建 線程(或自定義線程池),以 手柄‘長時間運行’的任務,並使用 短期任務的TPL或需要並行處理的任務 「。

奇怪的建議,或者你可能會誤引一點。一個線程也能夠並行處理,並且通過TPL,您可以使用LongRunning選項創建一個任務。剩下的是你不應該在ThreadPool上啓動長時間的任務。

什麼是長期運行的任務? 那是5秒,60秒,一小時 小時?

TPL運行在ThreadPool之上,TP將創建最大每秒2個新線程。因此,長期運行爲> = 500毫秒


即使有強大的硬件,是不是 那裏,我可以推 過高的工作量成任何 線程池/工作項隊列中的機會我有,

是的,沒有螺紋車刀可以擴展你的實際容量...

用20K的客戶,你可能會需要一個服務器場,一個選項,在您的設計包括EA rly ...

所以你應該在深入socket之前給WCF一個好看。

+0

現在我認爲線程調度程序並不總是尊重「LongRunning」選項?感謝有關「長期運行」的信息。 – slashp 2011-03-13 09:50:13

+0

@slas LongRunning是調度程序的一個提示,所以我認爲它可以自由地忽略它,整個「沒有LongRunning」的事情是關於(不)擾亂調度程序/負載平衡。 – 2011-03-13 09:54:12

+1

儘管LongRunning被定義爲一個提示,但默認TaskScheduler中的實際實現將始終創建一個新線程 - 請參閱http://coderkarl.wordpress.com/2012/12/13/long-running-tasks-and-threads/ – Lummo 2014-03-27 10:44:37

7

Marcs的建議是我會這樣做的。但是,如果你的任務花費的時間超過一秒,而且客戶端每秒發送一個請求,隊列會穩步增加。

在這種情況下,我會使用一個服務器作爲facade,它從客戶端獲取所有請求並以異步方式將響應發回給它們。

服務器會將所有請求放入一個由其他幾個服務器讀取的消息隊列中。這些服務器處理這些請求並將響應放入由第一個服務器讀取的另一個消息隊列中。

另一種解決方案是使用負載均衡服務器。

4

您似乎在構建一個服務器,它將服務數千個併發請求,每個請求長達數分鐘到數小時。

通常,使線程工作負載足夠短以在幾秒鐘內完成最多。再長一點,你會開始佔用服務器資源,並嚴重影響你的服務器的可伸縮性。在長時間運行的操作中阻塞數以萬計的線程,或者同時執行這些長時間運行的操作,將無可避免地影響您的可伸縮性。

不確定在每次長時間運行中消耗多少CPU時間。這會影響你的設計,例如:

如果每個長時間運行主要是在I/O上阻塞,則可以使用一個線程等待重疊的I/O或I/O完成端口,然後喚醒新線程到處理完成的I/O(達到限制的限制)。您需要有一定數量的線程來服務等待的連接。

如果每個長時間運行的操作等待其他操作完成,請考慮Windows Workflow Foundation。

如果每個長時間運行的操作消耗CPU,您不希望它們中的太多運行在任何時候,否則會導致服務器癱瘓。在這種情況下,使用MSMQ和/或TPL排隊任務,並確保只有少數人同時運行。

在所有這些中,似乎是保持打開客戶端連接。最糟糕的情況是爲每個連接保留一個線程阻塞。您需要實施線程池策略,以便僅使用有限數量的線程來服務所有未完成的連接。

+0

很好的回覆,謝謝你的信息。 – slashp 2011-03-13 10:05:08

相關問題