2013-10-02 19 views
2

我有一個線程在一個UDP套接字上偵聽,但也需要在一段時間內喚醒來執行其他任務。這些任務是由時間的推移或其他線程上的活動觸發的。我目前的設計是使用select()超時值作爲調度計時器,並且在需要將它從另一個線程中喚醒時將數據包寫入套接字(回送)地址。iOS選擇與kqueue/kevent對比mach_wait_until調度

但是,蘋果的文檔說select()超時不應該用來喚醒超過每秒幾次。而且,實際上,我發現它們可能會延遲100毫秒或更長,而我想要10-20毫秒的分辨率。他們只是試圖阻止CPU密集輪詢,或者使用select()本身有什麼問題。有更好的方法嗎?

是否有助於用kqueue/kevent替換select?或者,用mach_wait_until()來處理定時器,然後寫入套接字來喚醒網絡線程,創建一個專用調度線程?或者,在專用線程中完成所有工作,並將網絡線程隊列傳入數據發送給它?

回答

0

有些東西讓我誤解了這種方法。爲什麼你在select()線程上發生任何事情?

如果您需要一個專用於等待傳入數據包的線程,它們會盡可能地等待該線程。

while (1) { 
    int numsockets = select(…); 
    if (numsockets > 0) { 
     // Read data (only drain the socket) 
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
      // Process data 
     }); 
    } 
} 

然後,您可以使用timer dispatch sources運行定期任務。

dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, 
                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); 

dispatch_source_set_event_handler(source, ^{ 
    // Periodic process data 
}); 

uint64_t nsec = 0.001 * NSEC_PER_SEC; 
dispatch_source_set_timer(source, dispatch_time(DISPATCH_TIME_NOW, nsec), nsec, 0); 
dispatch_resume(source); 

我不知道,但我一直在說,你甚至可以使用派遣源來代替select()方法。

+0

實質上,與包IO相關的操作需要在單個線程上進行串行化。將這些操作排列到正在進行讀取的線程似乎更有效,因爲操作通常會由讀取觸發,因此不需要上下文切換。你的方法似乎需要在每次讀取時需要2個線程上下文切換,其中select()在調度隊列中醒來並且秒鐘在其上。我對Apple建議的感覺是,他們只是想最大限度地減少上下文切換,而不是從select中喚醒有任何問題,如果有人需要喚醒 – user1055568

+0

我理解您的擔憂,但我認爲這可能是一個過早優化的情況。使用'dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)'將確保在經常使用的隊列上調度該塊。上下文切換將成爲沉沒成本,因爲其他塊將在同一隊列中運行。如果全部使用並使用調度源來替換select(),那麼它將被分配到相同的隊列中,從而完全消除了「select()」線程。 –

+0

我想如果一個線程已經運行並且可以完成工作本身,那麼將工作分派到不同的線程很少有意義。這似乎是過早的複雜而不是過早的優化:-)。如果你的設計對我來說有意義,那麼工作可能需要很長時間,或者阻塞其他系統資源,讓數據堆積在套接字中。我有時會做這種工作,但是在更新管理數據包協議所需的狀態之後,我將它排隊到另一個線程。 – user1055568