从两个线程在同一个阻塞套接字上调用recv()
如果我有一个套接字 s
,当前没有可用的数据,它是一个阻塞套接字,并且我同时从两个线程调用 recv
,会发生什么情况? 其中一个线程会获取数据吗? 双方都会得到吗? 第二次调用 recv
会返回错误吗?
What happens if I have one socket, s
, there is no data currently available on it, it is a blocking socket, and I call recv
on it from two threads at once? Will one of the threads get the data? Will both get it? Will the 2nd call to recv
return with an error?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
一个线程会得到它,但没有办法知道是哪一个。
这似乎不是一个合理的设计。 是否需要两个线程在同一个套接字上调用
recv()
?One thread will get it, and there's no way to tell which.
This doesn't seem like a reasonable design. Is there a reason why you need two threads calling
recv()
on the same socket?套接字实现应该是线程安全的,因此只有一个线程应该在数据可用时获取数据。 另一个调用应该被阻止。
Socket implementations should be thread-safe, so exactly one thread should get the data when it becomes available. The other call should just block.
我找不到这方面的参考,但这是我的理解:
供应商对线程安全的保证可能只意味着多个线程可以安全地使用它们自己的套接字; 它不保证单个调用的原子性,也不保证在多个线程之间对套接字数据进行任何特定的分配。
假设线程 A 在正在以高速率接收 TCP 数据流的套接字上调用 recv()。 如果recv()需要是一个原子调用,那么线程A可能会阻止所有其他线程执行,因为它需要连续运行以提取所有数据(无论如何,直到它的缓冲区已满)。好的。 因此,我不会假设 recv() 不受上下文切换的影响。
相反,假设线程 A 在 TCP 套接字上对 recv() 进行阻塞调用,并且数据缓慢传入。 因此,对recv() 的调用返回时,errno 设置为EAGAIN。
在这两种情况下,假设线程 B 在同一套接字上调用 recv(),而线程 A 仍在接收数据。 线程 A 何时停止获取传递给它的数据以便线程 B 可以开始接收数据? 我不知道有哪个 Unix 实现会尝试记住线程 A 正在对套接字进行操作; 相反,由应用程序(线程 A 和 B)来协商其对它的使用。
一般来说,最好设计应用程序,以便只有一个线程会在单个套接字上调用 recv()。
I can't find a reference for this, but here's my understanding:
A vendor's guarantee of thread-safety may mean only that multiple threads can each safely use their own sockets; it does not guarantee atomicity across a single call, and it doesn't promise any particular allocation of the socket's data among multiple threads.
Suppose thread A calls recv() on a socket that's receiving TCP data streaming in at a high rate. If recv() needs to be an atomic call, then thread A could block all other threads from executing, because it needs to be running continuously to pull in all the data (until its buffer is full, anyway.) That wouldn't be good. Hence, I would not assume that recv() is immune to context switching.
Conversely, suppose thread A makes a blocking call to recv() on a TCP socket, and the data is coming in slowly. Hence the call to recv() returns with errno set to EAGAIN.
In either of these cases, suppose thread B calls recv() on the same socket while thread A is still receiving data. When does thread A stop getting data handed to it so that thread B can start receiving data? I don't know of a Unix implementation that will try to remember that thread A was in the middle of an operation on the socket; instead, it's up to the application (threads A and B) to negotiate their use of it.
Generally, it's best to design the app so that only one of the threads will call recv() on a single socket.
来自recv上的手册页
假设您正在使用 TCP,因为问题中未指定它。 因此,假设线程 A 和线程 B 都在套接字 s 的 recv() 上阻塞。 一旦 s 接收到一些数据,它将解锁其中一个线程(假设为 A),并返回数据。 就我们而言,返回的数据将具有某种随机大小。 线程 A 检查接收到的数据并确定它是否具有完整的“消息”,其中消息是应用程序级别的概念。
线程 A 认为它没有完整的消息,因此它再次调用 recv()。 但与此同时,B 已经在同一个套接字上阻塞,并且已经收到了用于线程 A 的其余“消息”。我在这里松散地使用预期。
现在,线程 A 和线程 B 都有一条不完整的消息,并且根据代码的编写方式,会将数据视为无效而丢弃,或者导致奇怪且微妙的错误。
我希望我能说我从经验中不知道这一点。
因此,虽然 recv() 本身在技术上是线程安全的,但如果将其用于 TCP,那么让两个线程同时调用它是一个坏主意。
据我所知,使用 UDP 是完全安全的。
我希望这有帮助。
From the man page on recv
Lets assume you are using TCP, since it was not specified in the question. So suppose you have thread A and thread B both blocking on recv() for socket s. Once s has some data to be received it will unblock one of the threads, lets say A, and return the data. The data returned will be of some random size as far as we are concerned. Thread A inspects the data received and decides if it has a complete "message", where a message is an application level concept.
Thread A decides it does not have a complete message, so it calls recv() again. BUT in the meantime B was already blocking on the same socket, and has received the rest of the "message" that was intended for thread A. I am using intended loosely here.
Now both thread A and thread B have an incomplete message, and will, depending on how the code is written, throw the data away as invalid, or cause weird and subtle errors.
I wish I could say I didn't know this from experience.
So while recv() itself is technically thread safe, it is a bad idea to have two threads calling it simultaneously if you are using it for TCP.
As far as I know it is completely safe when you are using UDP.
I hope this helps.