我正在尝试基于领导者/关注者并发体系结构实现基本服务器。 我最初试图创建一个线程池并创建一个线程队列,但在阅读了这个答案(Explain "Leader/Follower" Pattern)之后,我觉得我的算法不正确,因为所选答案声称不需要互斥锁。
领导者/追随者背后的想法是,你初始化一个线程池,一个线程作为“领导”线程,其余的是“跟随者”线程。在服务器中,领导者线程将监听传入连接,而跟随者线程将全部休息。当领导者线程检测到传入连接时,其中一个静止跟随者线程将被提升为新的领导者线程,而前一个领导者接受连接并提供请求。当前领导者线程完成为请求提供服务时,它将成为一个休息的跟随者线程。
但是,如果不使用互斥锁和条件变量,我无法看到任何实现方法。目前我的实现使用线程池,只允许每个线程接受互斥锁中的新连接。
有人可以提供关于领导者/粉丝实施的高级别说明吗?
以下是我编写的当前实现的一些代码。
#define THREAD_COUNT 10
pthread_mutex_t request_tx;
int main(int argc, char* argv[])
{
pthread_t threadA[THREAD_COUNT];
pthread_mutex_init(&request_tx, NULL);
//initialize server socket stuff
for (int i = 0; i < THREAD_COUNT; ++i)
{
pthread_create(&threadA[i], NULL, rest, NULL);
}
for (int i = 0; i < THREAD_COUNT; ++i)
pthread_join(threadA[i], NULL);
return 0;
}
void* rest(void* kargs)
{
int client_fd;
struct sockaddr_in cli_addr;
socklen_t sin_t = sizeof(cli_addr);
while (1)
{
pthread_mutex_lock(&request_tx);
client_fd = accept(server_fd, (struct sockaddr*) &cli_addr, &sin_t);
if (client_fd > 0)
serve(client_fd);
else
pthread_mutex_unlock(&request_tx);
}
}
void serve(int client_fd)
{
pthread_mutex_unlock(&request_tx);
// serve request here
...
}
答案 0 :(得分:2)
链接的答案是错误的。同步绝对是必要的某处。在您的示例中,您可以删除互斥锁,因为accept
将在内核中阻塞,并且(大多数?)实现将仅为传入连接激活一个线程。但是,内核中仍然存在一些同步。
您的示例有点误导,因为领导者/关注者模式通常用于在线程池的多个连接上分发单个请求,而不是连接。在这一点上,事情变得相当复杂,因为已完成处理的追随者需要告诉领导者新连接需要观看。如果您受限于标准POSIX接口,则可能非常复杂。 (使用epoll
,应该可以将大部分复杂性移交给内核。)
总的来说,我会谨慎地采用90年代的事件处理模式。从那时起,架构发生了重大变化:NUMA机器不再罕见,系统可以非常舒适地处理单个进程中的数万个线程,并且可以使用epoll
等其他事件处理接口。