Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在使用epoll_wait时正确读取数据 我试图将一个使用IOCP的现有Windows C++代码移植到Linux。在决定使用epoll\u wait来实现高并发性之后,我已经面临一个理论问题,即何时尝试处理接收到的数据_C++_Linux_Multithreading_Recv_Epoll - Fatal编程技术网

如何在使用epoll_wait时正确读取数据 我试图将一个使用IOCP的现有Windows C++代码移植到Linux。在决定使用epoll\u wait来实现高并发性之后,我已经面临一个理论问题,即何时尝试处理接收到的数据

如何在使用epoll_wait时正确读取数据 我试图将一个使用IOCP的现有Windows C++代码移植到Linux。在决定使用epoll\u wait来实现高并发性之后,我已经面临一个理论问题,即何时尝试处理接收到的数据,c++,linux,multithreading,recv,epoll,C++,Linux,Multithreading,Recv,Epoll,想象一下,两个线程调用epoll\u wait,并收到两条consequetives消息,这样Linux就可以解锁第一个线程,很快就会解锁第二个线程 例如: Thread 1 blocks on epoll_wait Thread 2 blocks on epoll_wait Client sends a chunk of data 1 Thread 1 deblocks from epoll_wait, performs recv and tries to process data Clien

想象一下,两个线程调用
epoll\u wait
,并收到两条consequetives消息,这样Linux就可以解锁第一个线程,很快就会解锁第二个线程

例如:

Thread 1 blocks on epoll_wait
Thread 2 blocks on epoll_wait
Client sends a chunk of data 1
Thread 1 deblocks from epoll_wait, performs recv and tries to process data
Client sends a chunk of data 2
Thread 2 deblocks, performs recv and tries to process data.
这种情况可以想象吗?也就是说,它会发生吗


有没有办法防止这种情况发生,从而避免在recv/处理代码中实现同步?

通常,当您有一个线程侦听单个异步源上的数据时,会使用
epoll
。为了避免繁忙的等待(手动轮询),您可以使用
epoll
让您知道数据何时准备就绪(很像
select
does)

从单个数据源中读取多个线程并不是标准的实践,至少我会认为它是一个坏的实践。
如果要使用多个线程,但只有一个输入源,然后指定其中一个线程来侦听数据并对数据进行排队,以便其他线程可以从队列中读取各个片段。

我相信,使用epoll和每个核心一个线程的高性能软件会创建多个epoll句柄,每个句柄处理所有连接的一个子集。通过这种方式,工作被划分,但您描述的问题被避免。

如果您有多个线程从同一组epoll句柄读取数据,我建议您使用
EPOLLONESHOT
将epoll句柄置于一次性级别触发模式。这将确保在一个线程观察到触发的句柄之后,在您使用
epoll_ctl
重新配置句柄之前,没有其他线程会观察到它

如果需要独立处理读写路径,则可能需要完全拆分读写线程池;一个epoll句柄用于读取事件,一个用于写入事件,并将线程以独占方式分配给其中一个。此外,对读路径和写路径有一个单独的锁。当然,在修改每个套接字的状态时,必须注意读线程和写线程之间的交互

如果您确实使用这种拆分方法,则需要考虑如何处理套接字闭包。很可能您需要一个额外的共享数据锁,以及在共享数据锁下为读写路径设置的“确认关闭”标志。然后,读线程和写线程可以竞相确认,最后一个确认的线程可以清理共享数据结构。就是这样,

void OnSocketClosed(shareddatastructure *pShared, int writer)
{
  epoll_ctl(myepollhandle, EPOLL_CTL_DEL, pShared->fd, NULL);
  LOCK(pShared->common_lock);
  if (writer)
    pShared->close_ack_w = true;
  else
    pShared->close_ack_r = true;

  bool acked = pShared->close_ack_w && pShared->close_ack_r;
  UNLOCK(pShared->common_lock);

  if (acked)
    free(pShared);
}

我在这里假设您试图处理的情况如下:

void OnSocketClosed(shareddatastructure *pShared, int writer)
{
  epoll_ctl(myepollhandle, EPOLL_CTL_DEL, pShared->fd, NULL);
  LOCK(pShared->common_lock);
  if (writer)
    pShared->close_ack_w = true;
  else
    pShared->close_ack_r = true;

  bool acked = pShared->close_ack_w && pShared->close_ack_r;
  UNLOCK(pShared->common_lock);

  if (acked)
    free(pShared);
}
您有多个(可能非常多个)套接字,希望同时从中接收数据

您希望在第一次接收到线程A上第一个连接的数据时开始处理该数据,然后确保在线程A中处理完该连接的数据之前,不会在任何其他线程上处理该数据

当您这样做时,如果现在在不同的连接上接收到一些数据,您希望线程B拾取该数据并处理它,同时仍然确保在线程B处理完该连接之前没有其他人可以处理该连接,等等

在这种情况下,在多个线程中使用同一个epoll fd的epoll_wait()是一种相当有效的方法(我并不是说它一定是最有效的)

这里的技巧是使用epolonneshot标志将各个连接fd添加到epoll fd。这可以确保一旦从epoll_wait()返回fd,它将不受监视,直到您明确告诉epoll再次监视它为止。这确保处理此连接的线程不会受到干扰,因为在该线程标记要再次监视的连接之前,其他线程都不能处理同一连接

您可以使用epoll_ctl()和epoll_ctl_MOD将fd设置为再次监控EPOLLIN或EPOLLOUT

在多线程中使用这样的epoll的一个显著好处是,当一个线程完成连接并将其添加回epoll监控集中时,仍然在epoll_wait()中的任何其他线程甚至在前一个处理线程返回epoll_wait()之前都会立即监控它。顺便说一句,如果另一个线程现在立即拾取该连接(因此需要获取该连接的数据结构并刷新前一个线程的缓存),这也可能是一个缺点,因为缺少缓存数据局部性。什么最有效取决于您的确切使用模式


如果您试图在不同的线程中处理随后在同一连接上接收到的消息,那么使用epoll的方案将不适合您,使用侦听线程向有效队列馈送工作线程的方法可能更好。

前面的回答指出调用epoll\u wait()“从多个线程调用”是一个坏主意,你几乎肯定是对的,但我对这个问题很感兴趣,所以我尝试找出当它从同一个句柄上的多个线程调用,等待同一个套接字时会发生什么。我编写了以下测试代码:

#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

struct thread_info {
  int number;
  int socket;
  int epoll;
};

void * thread(struct thread_info * arg)
{
    struct epoll_event events[10];
    int s;
    char buf[512];

    sleep(5 * arg->number);
    printf("Thread %d start\n", arg->number);

    do {
        s = epoll_wait(arg->epoll, events, 10, -1);

        if (s < 0) {
            perror("wait");
            exit(1);
        } else if (s == 0) {
            printf("Thread %d No data\n", arg->number);
            exit(1);
        }
        if (recv(arg->socket, buf, 512, 0) <= 0) {
            perror("recv");
            exit(1);
        }
        printf("Thread %d got data\n", arg->number);
    } while (s == 1);

    printf("Thread %d end\n", arg->number);

    return 0;
}

int main()
{
    pthread_attr_t attr;
    pthread_t threads[2];
    struct thread_info thread_data[2];
    int s;
    int listener, client, epollfd;
    struct sockaddr_in listen_address;
    struct sockaddr_storage client_address;
    socklen_t client_address_len;
    struct epoll_event ev;

    listener = socket(AF_INET, SOCK_STREAM, 0);

    if (listener < 0) {
        perror("socket");
        exit(1);
    }

    memset(&listen_address, 0, sizeof(struct sockaddr_in));
    listen_address.sin_family = AF_INET;
    listen_address.sin_addr.s_addr = INADDR_ANY;
    listen_address.sin_port = htons(6799);

    s = bind(listener,
             (struct sockaddr*)&listen_address,
             sizeof(listen_address));

    if (s != 0) {
        perror("bind");
        exit(1);
    }

    s = listen(listener, 1);

    if (s != 0) {
        perror("listen");
        exit(1);
    }

    client_address_len = sizeof(client_address);
    client = accept(listener,
                    (struct sockaddr*)&client_address,
                    &client_address_len);

    epollfd = epoll_create(10);
    if (epollfd == -1) {
        perror("epoll_create");
        exit(1);
    }

    ev.events = EPOLLIN;
    ev.data.fd = client;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, client, &ev) == -1) {
        perror("epoll_ctl: listen_sock");
        exit(1);
    }

    thread_data[0].number = 0;
    thread_data[1].number = 1;
    thread_data[0].socket = client;
    thread_data[1].socket = client;
    thread_data[0].epoll = epollfd;
    thread_data[1].epoll = epollfd;

    s = pthread_attr_init(&attr);
    if (s != 0) {
        perror("pthread_attr_init");
        exit(1);
    }

    s = pthread_create(&threads[0],
                       &attr,
                       (void*(*)(void*))&thread,
                       &thread_data[0]);

    if (s != 0) {
        perror("pthread_create");
        exit(1);
    }

    s = pthread_create(&threads[1],
                       &attr,
                       (void*(*)(void*))&thread,
                       &thread_data[1]);

    if (s != 0) {
        perror("pthread_create");
        exit(1);
    }

    pthread_join(threads[0], 0);
    pthread_join(threads[1], 0);

    return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
结构线程信息{
整数;
int插座;
int epoll;
};
void*线程(结构线程信息*arg)
{
结构epoll_事件事件[10];
int-s;
char-buf[512];
睡眠(5*arg->number);
printf(“线程%d开始\n”,参数->编号);
做{
s=epoll\u wait(arg->epoll,事件,10,-1);
如果(s<0){
佩罗(“等待”);
出口(1);
}如果(s==0),则为else{
printf(“线程%d无数据\n”,参数->编号);
出口(1);
}
如果(记录