2012-02-26 84 views
1

我目前正在使用的應用程序是一個服務器,它將使用select()來管理與客戶端的連接,每次服務器收到消息時,它都會打開一個新線程以讀取套接字。在此期間,套接字的文件描述符將從該集中刪除,並將在讀取結束時添加。 下面是代碼的樣本是FD_SET,FD_CLR ...原子操作嗎?

struct s_handle { 
int sock; 
fd_set * rdfs; 
}; 




int main(){ 
... 
fd_set rdfs; 
... 
while(1){ 
.... 
    select(nb_fd,&rdfs,NULL,NULL,NULL) 
    for_each(peer){ 
    if(FD_ISSET(peer->sock,&rdfs)){ 
    struct s_handle * h = malloc(sizeof(struct s_handle)); 
    h->sock = peer->sock; 
    h->rdfs = &rdfs; 
    FD_CLR(peer->sock,&rdfs); 
    pthread_create(thread,NULL,handle,(void *)&h); 
    } 
    } 
... 
} 
... 
} 

void* handle(void* argss){ 
struct s_handle * temp = (struct s_handle *) argss; 
... 
FD_SET(temp->sock,temp->rdfs); 
} 

不FD_SET,FD_ISSET和FD_CLR是原子操作,或者我需要用互斥鎖RDFS?

如果需要互斥鎖,我該如何避免死鎖?

回答

3

首先,你不應該創建那樣的線程。創建一個線程是一個相當高的開銷操作,應該只在需要更多線程時使用,這不僅僅是因爲你需要做更多的工作。

是的,你需要用互斥鎖保護FD_*函數。通常的解決方案是隻有在您執行FD_*操作所需的一秒鐘時間內擁有一個互斥鎖。在調用select之前,您獲取互斥鎖,複製描述符集,然後釋放互斥鎖。

一般來說,從讀取集合中刪除套接字是一個壞主意。將套接字放回到讀取集中不會更改後面已經出現的select。而且你將會搞砸如何讓select撥出select以便在新的集合上操作。

您可能想重新考慮您的I/O發現方法,並使用其中一種標準方法,而不是嘗試自己推出。由於最近讀取的是select仍然被阻止,或者在您從每個套接字讀取完畢後必須重新登錄select,因此您要強制執行醜陋的權衡,因爲有些套接字沒有被聽取閱讀。這兩種解決方案都不好。

一個更常見的模式是在讀取它時保持套接字在集合中,並且不會返回到select,直到從所有套接字讀取(但未必處理其數據)爲止。

1

不能保證它們是,而且你也不應該依賴它。

fd_set s通常用簡單的位圖(int或類似的數組)實現,並且FD_*宏只是位操作操作。這些操作在平板原生int以上的任何時候都很少原子化(如果偶數的話)。

1

FD_SET,FD_ISSET等是宏 - 我不認爲你可以指望他們是原子。但是,我不建議使用互斥體,而是建議重新考慮您的方法;每當需要閱讀時創建一個新線程,並從不同於使用其結果的線程調用select看起來都是不好的想法。如果您爲什麼需要使用單獨的線程只是爲了讀取而不是完全處理該連接,那麼爲什麼不讓它自己擁有自己的fd_set並且自己調用select本身?


在重讀的問題,我會說,如果你做使用你現在的辦法,你要注意,你讀的fd_set需要每次調用select前重建,並通過修改呼叫。因此,線程函數結束時的FD_SET無論如何都是無用的,因爲如果描述符尚未準備好再次讀取,則該描述符將從下一個select調用中的集合中移除。您需要在調用select之前在您的select線程中構建您的讀取集合 - 因此您需要另一種方法來確定需要包含哪些套接字。

你可以添加一個忙標誌您s_handle結構,和後衛與互斥 - 你會然後將其設置在創建線程之前,並紛紛跟帖清楚它退出時。在選擇呼叫之前填入讀取fd_set時,您需要爲s_handle結構中的標記未標記爲「繁忙」的每個對等點添加套接字。

+0

如果你讓這個線程擁有自己的'fd_set'並且調用'select'本身,那麼這個線程必須呆在等待I/O。然後,在該特定線程安排之前,無法在該套接字上完成任何工作。所以你會消耗大量額外的資源,所有這些線程都會等待,並且需要大量額外的上下文切換來獲得正確的線程運行。 (但是,考慮到他使用'select',他可能不關心性能。) – 2012-02-26 22:15:52

+0

我寫這些時有點困惑 - 它沒有完全點擊它產生一個線程來讀取,然後退出而不放棄。我正在考慮創建一個線程來讀取套接字,並保持這個套接字直到連接關閉 - 儘管看起來不像他正在做的那樣。 – Dmitri 2012-02-26 22:41:21