如何在 Windows 上取消 select() 中的等待

发布于 2024-09-11 13:13:18 字数 668 浏览 2 评论 0原文

在我的程序中,有一个线程(接收线程)负责从 TCP 套接字接收请求,并且有许多线程(工作线程)负责处理接收到的请求。处理请求后,我需要通过 TCP 发送答案。

这是一个问题。我想在用于接收数据的同一个线程中发送 TCP 数据。该线程接收数据后通常会在select()中等待新数据。因此,一旦工作线程处理完请求并将答案放入输出队列中,它就必须向接收线程发出信号,表明有数据要发送。问题是我不知道如何取消 select() 中的等待,以便摆脱等待并调用 send()

或者我应该使用另一个线程专门通过 TCP 发送数据吗?

已更新

MSalters、Artyom 感谢您的回答!

MSalters,读完你的答案后,我找到了这个网站: Winsock 2 I/O 方法 并阅读关于WSAWaitForMultipleEvents()。事实上,我的程序必须同时在 HP-UX 和 Windows 上运行,我最终决定使用 Artyom 建议的方法。

In my program there is one thread (receiving thread) that is responsible for receiving requests from a TCP socket and there are many threads (worker threads) that are responsible for processing the received requests. Once a request is processed I need to send an answer over TCP.

And here is a question. I would like to send TCP data in the same thread that I use for receiving data. This thread after receiving data usually waits for new data in select(). So once a worker thread finished processing a request and put an answer in the output queue it has to signal the receiving thread that there are data to send. The problem is that I don't know how to cancel waiting in select() in order to get out of waiting and to call send() .

Or shall I use another thread solely for sending data over TCP?

Updated

MSalters, Artyom thank you for you answers!

MSalters, having read your answer I found this site: Winsock 2 I/O Methods and read about WSAWaitForMultipleEvents(). My program in fact must work both on HP-UX and Windows I finally decided to use the approach that had been suggested by Artyom.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

顾冷 2024-09-18 13:13:19

您需要使用类似于安全管道技巧的东西,但在您的情况下,您需要使用一对连接的 TCP 套接字。

  1. 创建一对套接字。
  2. 将一个添加到选择中并等待它,并
  3. 通过从其他线程写入其他套接字来通知。
  4. 当其中一个套接字可读时,Select 立即被唤醒,读取所有套接字
    这个特殊套接字中的数据并检查队列中的所有数据以发送/接收

如何在Windows下创建套接字对?

inline void pair(SOCKET fds[2])
{
    struct sockaddr_in inaddr;
    struct sockaddr addr;
    SOCKET lst=::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
    memset(&inaddr, 0, sizeof(inaddr));
    memset(&addr, 0, sizeof(addr));
    inaddr.sin_family = AF_INET;
    inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    inaddr.sin_port = 0;
    int yes=1;
    setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes));
    bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr));
    listen(lst,1);
    int len=sizeof(inaddr);
    getsockname(lst, &addr,&len);
    fds[0]=::socket(AF_INET, SOCK_STREAM,0);
    connect(fds[0],&addr,len);
    fds[1]=accept(lst,0,0);
    closesocket(lst);
}

当然应该为返回值添加一些检查。

You need to use something similar to safe-pipe trick, but in your case you need to use a pair of connected TCP sockets.

  1. Create a pair of sockets.
  2. Add one to the select and wait on it as well
  3. Notify by writing to other socket from other threads.
  4. Select is immediately waken-up as one of the sockets is readable, reads all the
    data in this special socket and check all data in queues to send/recv

How to create pair of sockets under Windows?

inline void pair(SOCKET fds[2])
{
    struct sockaddr_in inaddr;
    struct sockaddr addr;
    SOCKET lst=::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
    memset(&inaddr, 0, sizeof(inaddr));
    memset(&addr, 0, sizeof(addr));
    inaddr.sin_family = AF_INET;
    inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    inaddr.sin_port = 0;
    int yes=1;
    setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes));
    bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr));
    listen(lst,1);
    int len=sizeof(inaddr);
    getsockname(lst, &addr,&len);
    fds[0]=::socket(AF_INET, SOCK_STREAM,0);
    connect(fds[0],&addr,len);
    fds[1]=accept(lst,0,0);
    closesocket(lst);
}

Of course some checks should be added for return values.

一笔一画续写前缘 2024-09-18 13:13:19

select 不是 Windows 的本机 API。本机方式是 WSAWaitForMultipleEvents。如果您使用它来创建可警报的等待,则可以使用 QueueUserAPC 来指示等待线程发送数据。 (这也可能意味着您不必实现自己的输出队列)

select is not the native API for Windows. The native way is WSAWaitForMultipleEvents. If you use this to create an alertable wait, you can use QueueUserAPC to instruct the waiting thread to send data. (This might also mean you don't have to implement your own output queue)

你是年少的欢喜 2024-09-18 13:13:19

另请参阅这篇文章:
如何通知 select() 立即返回?

对于 unix,请使用匿名管道。对于 Windows:
解除阻塞可以通过向 fd_set 添加一个虚拟(未绑定)数据报套接字然后关闭它来实现。为了使这个线程安全,请使用 QueueUserAPC:

我发现使这个多线程安全的唯一方法是在运行 select 语句的同一线程中关闭并重新创建套接字。当然,如果线程在选择上阻塞,这会很困难。然后在windows进来调用QueueUserAPC。当 windows 在 select 语句中阻塞时,线程可以处理异步过程调用。您可以使用 QueueUserAPC 从不同的线程安排此操作。 Windows 中断 select,在同一线程中执行您的函数,然后继续执行 select 语句。您现在可以在 APC 方法中关闭套接字并重新创建它。保证线程安全,您永远不会丢失信号。

See also this post:
How to signal select() to return immediately?

For unix, use an anonymous pipe. For Windows:
Unblocking can be achieved by adding a dummy (unbound) datagram socket to fd_set and then closing it. To make this thread safe, use QueueUserAPC:

The only way I found to make this multi-threadsafe is to close and recreate the socket in the same thread as the select statement is running. Of course this is difficult if the thread is blocking on the select. And then comes in the windows call QueueUserAPC. When windows is blocking in the select statement, the thread can handle Asynchronous Procedure Calls. You can schedule this from a different thread using QueueUserAPC. Windows interrupts the select, executes your function in the same thread, and continues with the select statement. You can now in your APC method close the socket and recreate it. Guaranteed thread safe and you will never loose a signal.

高跟鞋的旋律 2024-09-18 13:13:19

典型的模型是由工人处理自己的写作。您是否有理由希望通过 select 线程发送所有输出 IO?

如果您确定此模型,您可以让工作人员也使用文件描述符 (pipe(2)) 将数据发送回主线程,然后只需将这些描述符添加到您的 select 中() 调用。

而且,如果您特别确定不会使用管道将数据发送回主进程,则 select 调用允许您指定超时。您可以在检查工作线程时处于忙等待状态,并定期调用 select 来确定要读取哪些 TCP 套接字。

The typical model is for the worker to handle its own writing. Is there a reason why you want to send all the output-IO through selecting thread?

If you're sure of this model, you could have your workers send data back to the master thread using file descriptors as well (pipe(2)) and simply add those descriptors to your select() call.

And, if you're especially sure that you're not going to use pipes to send data back to your master process, the select call allows you to specify a timeout. You can busy-wait while checking your worker threads, and periodically call select to figure out which TCP sockets to read from.

走走停停 2024-09-18 13:13:19

另一个快速而肮脏的解决方案是将本地主机套接字添加到集合中。现在使用这些套接字作为线程间通信队列。每个工作线程只需向其套接字发送一些内容,该内容最终会到达接收线程中相应的套接字。这会唤醒 select(),然后您的接收线程可以在适当的传出套接字上回显该消息。

Another quick&dirty solution is to add localhost sockets to the set. Now use those sockets as the inter-thread communication queues. Each worker thread simply sends something to its socket, which ends up on the corresponding socket in your receiving thread. This wakes up the select(), and your receiving thread can then echo the message on the appropriate outgoing socket.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文