2012-02-26 36 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