0

大多數情況下,您會聽到每個多線程模型(非阻塞IO)比線程每個連接模型(阻塞IO)好得多。推理聽起來像「線程每連接方法創建太多的線程和大量的開銷與維護這麼多的線程相關聯」。但是這個開銷沒有解釋。爲什麼每個多連接模型被認爲比線程連接模型更好?

  • 常見的誤解是調度開銷與所有線程的數量相比較。但事實並非如此,調度開銷與可運行線程的數量相當。因此,在典型的IO綁定應用程序中,大多數線程實際上會在IO上被阻塞,並且只有其中的幾個線程可以運行 - 這與「每個多線程線程」模型沒有區別。
  • 至於上下文切換開銷,我認爲應該沒有區別,因爲當數據到達時,內核應該喚醒線程選擇器線程或連接線程。
  • 問題可能出在IO系統調用 - 內核可能比阻塞IO調用更好地處理kqueue/epoll調用。然而,這聽起來並不合理,因爲在數據到達時實施O(1)算法來選擇阻塞的線程應該不成問題。
  • 如果你有很多短命的連接,你會有很多短命的線程。而產生一個新的線程是一個昂貴的操作(是嗎?)。爲了解決這個問題,你可以創建線程池並仍然使用阻塞I/O。
  • 可能會產生操作系統限制的線程數,但它們可能會隨配置參數而改變。
  • 在多核系統中,假設不同的會話訪問相同的共享數據。如果我們正在討論每個線程連接模型,這可能會導致很多高速緩存一致性流量,並且可能會降低系統速度。但是,如果在給定時間點只有其中一個可運行,爲什麼不把所有這些線程都放在單核上?如果其中有一個以上是可運行的,則意味着它們應該安排在不同的內核上。但是,爲了在多線程連接模型中實現相同的性能,我們需要有多個選擇器,它們將在不同的內核上進行調度並訪問相同的共享數據。所以我沒有看到從緩存角度的差異。
  • 在GC環境中(以Java爲例),垃圾收集器應該通過遍歷GC根開始的對象圖來了解哪些對象可以訪問。 GC根包括線程堆棧。所以GC在這個圖表的第一層做更多的工作。但是,對於這兩種方法,此圖中活動節點的總數應該相同。所以從GC的角度來看沒有任何開銷。
  • 我同意,唯一的參數是每個線程都爲其堆棧消耗內存。但即使對於這種情況,如果它們不使用遞歸調用,我們可能會限制這些線程的堆棧大小。

您的想法是什麼?

+0

您剛剛添加的兩個新點也是正確的。請注意,非阻塞IO不會*暗示一個線程。通常,每個內核的一個線程可用於擴展服務器。基於單線程的「select」或「poll」設計基本已經過時。現代設計(如epoll,kqueue或IOCP)使用多個線程來排除IO事件隊列(基本上)。 – usr

+0

@usr,感謝您的回答,我在腦海中添加了新的觀點。我希望這個問題能夠幫助很多人意識到,它唯一的線程堆棧開銷會影響他們的決定。 – andrershov

+0

稻草人。我不知道有人聲稱每個連接的線程比你的標題慢,如果他們有錯誤的話。我同意的通常的說法是可疑的,NBIO,async等是更具擴展性的,這就是你的文章的實際內容。 – EJP

回答

1

有兩種費用:

  1. 堆棧存儲器。非阻塞IO(以任何形式使用它)保存堆棧內存。 IO現在只是一個小數據結構。
  2. 當負載較高時,減少上下文切換和內核轉換。然後,可以使用一個開關來處理多個完成的IO。

大多數服務器都沒有處於高負載狀態,因爲這會對負載尖峯造成很小的安全限制。因此,第(2)點主要與人工負荷相關,如基準(旨在證明一個觀點......)。

堆棧節省是99%的原因。

是否要權衡開發時間和代碼複雜度以節省內存取決於您擁有多少連接。在10個連接處,這不是一個問題。在10000個連接中,基於線程的模型變得不可行。

您在問題中陳述的觀點是正確的。

也許你被「常識」總是使用非阻塞套接字IO的事實弄糊塗了?事實上,這種(虛假的)宣傳正在網絡上的任何地方傳播。宣傳通過反覆做出相同的簡單陳述而發揮作用。

相關問題