4

我正試圖在我的腦海中構建一個基本上是併發下載管理器的Cocoa應用程序的最佳方式。應用程序會與之對話的一個服務器,用戶列出一大堆要下拉的內容,然後應用程序處理該列表。 (它不使用HTTP或FTP,所以我不能使用URL加載系統;我將通過套接字連接進行通信。)Cocoa中的併發網絡客戶端

這基本上是經典的生產者 - 消費者模式。訣竅是消費者的數量是固定的,而且是持久的。服務器對可以打開的同時連接數設置了嚴格限制(通常至少爲兩個),並且打開新連接的開銷很大,因此在理想的情況下,應用程序的整個生命週期內都會打開相同的N個連接。

解決這個問題的一種方法可能是創建N個線程,每個線程將「擁有」一個連接,然後等待請求隊列,阻止它爲空。由於連接數量永遠不會很大,所以在實際系統開銷方面這不是不合理的。但從概念上來說,Cocoa似乎必須提供更優雅的解決方案。

看來我可以使用NSOperationQueue,並調用setMaxConcurrentOperationCount:與連接數。然後我只是把下載請求扔進那個隊列。但在這種情況下,我不確定如何自己管理連接。 (只要把它們放在一個堆棧上,並且依靠隊列來確保我不會超出/低於運行?在dispatch semaphore連同堆棧一起投擲?)

現在我們處於勇敢的新世界Grand Central Dispatch,這是否打開了解決這個問題的其他方法?乍一看,它看起來並不像它,因爲GCD的動態擴展併發性的能力(並在蘋果的Changing Producer-Consumer Implementations建議中提到)實際上並不能幫助我。但我只是抓了一下閱讀的表面。

編輯:

在如此重要的情況:是的,我打算使用異步/非阻塞套接字的API做的與服務器的實際通信。所以I/O本身不一定要在它自己的線程上。我只關心排隊工作的機制,並且(安全地)將它放到連接處,因爲它們變得可用。

回答

0

爲後人的緣故,後在其他地方的一些討論,我想我會採用這種解決方案基本上是:

  • 具有掛起下載操作的隊列,初始爲空。
  • 有一組包含所有打開的連接,最初爲空。
  • 有一個可變數組(空閒,打開連接,最初爲空)。
  • 當用戶增加了一個下載請求:
    • 如果空閒連接的陣列是不爲空,刪除一個和下載分配給它。
    • 如果沒有空閒連接,但總連接數尚未達到其限制,請打開一個新連接,將其添加到該集中,併爲其分配下載。
    • 否則,請稍後排隊下載。
  • 當下載完成時:如果有排隊的請求,則將一個 退出並將其提供給連接;否則,將連接添加到空閒列表。

所有這些工作都將發生在主線程上。解碼每次下載結果的工作將被卸載到GCD,因此它可以處理併發性的限制,並且不會阻塞主線程。

打開一個新的連接可能需要一段時間,因此,創建一個新的可能是一點點在實際應用中更復雜(比如過程,排隊下載,發起連接過程,然後出隊它當連接完全建立)。但我仍然認爲我對競爭條件可能性的看法被誇大了。

1

如果你使用CFSocket的非阻塞I/O調用,我同意,應該都發生在主線程上,讓操作系統處理併發問題,因爲你只是在複製數據而不是真的在做任何計算。

除此之外,它聽起來像你的應用程序需要做的唯一的其他工作是保持一個項目隊列下載。當任何一個傳輸完成時,CFSocket回調可以啓動傳輸隊列中的下一個項目。 (如果隊列爲空,則減少連接數,並且如果將某些內容添加到空隊列中,則啓動新的傳輸。)我不明白爲什麼需要多個線程。可能你遺漏了一些重要的東西,但根據你的描述,這個應用程序是I/O綁定的,而不是CPU綁定的,所以所有併發的東西都會使代碼變得更復雜,對性能影響最小。

在主線程中執行所有操作。

+1

該應用程序幾乎肯定不受CPU限制。下載完成後會有一些解碼任務,但我可以將它們發送給GCD進行處理。我主要關注的問題是1)保持UI的響應,2)避免任何競爭條件下工作。如果I/O回調函數全部在主線程上運行,這通常可以解決(2),但是我想知道它是以(1)爲代價的。據推測,大部分工作只是將東西添加到緩衝區中,而且應該很快。 – 2009-09-30 00:05:48