我有 20 个线程一次在单个 tcp 套接字上发送数据并接收数据。当我运行我的应用程序时,我没有看到任何同步问题,但根据我的理解,当两个线程同时尝试写入 tcp 套接字或一个线程正在写入而另一个线程正在读取时,可能会出现一些问题。
如果我的理解是正确的,为什么我不会遇到任何错误?
我有 20 个线程一次在单个 tcp 套接字上发送数据并接收数据。当我运行我的应用程序时,我没有看到任何同步问题,但根据我的理解,当两个线程同时尝试写入 tcp 套接字或一个线程正在写入而另一个线程正在读取时,可能会出现一些问题。
如果我的理解是正确的,为什么我不会遇到任何错误?
有时,当您在过马路前不向两边看时,您仍然可以安全地到达马路的另一边。这并不意味着每次你做它都会成功。
事情是这样的,你说“你没有看到任何同步问题”,但这只是因为它恰好做了你想让它做的事情。反过来——你看不到任何同步问题的原因是你碰巧希望它做它碰巧做的事情。期望它做其他事情的人会看到使用相同代码的同步问题。
换句话说,你掷了一枚可能正面或反面的硬币。你期望它会出现,知道它不能保证。它出现了。这并不神秘——解释是你预料到它会发生什么。如果你期待别的东西,即使它做了同样的事情,它也不会达到你的预期。
首先,每个套接字的发送和接收流是独立的。一个线程发送而另一个线程接收应该没有问题。
如果多个线程尝试写入一个套接字,则行为通常是未定义的。实际上,来自一个线程的写调用将首先进入 TCP 堆栈状态机中的锁,防止任何其他线程进入、写入其数据、释放锁并退出堆栈,因此允许来自其他线程的写调用线程继续。这将允许对单个写入调用进行序列化。如果您的协议实现可以通过一次写入调用发送所有 PDU,那么很好。如果一个 PDU 需要多个写入调用,那么当来自多个线程的写入调用交错时,您的传出 PDU 可能会被分割。
从多个线程对一个套接字进行接收调用只是……一些东西。即使堆栈内部同步一次只允许每个套接字一次接收调用,TCP 的流式传输特性肯定会以伪任意方式在线程之间拆分接收到的数据。只是不要这样做,这太疯狂了。
TCP 已经有了一种多路复用数据流的机制——多个套接字。您应该正确使用它们。
如果你需要在一个套接字上多路复用数据流,你应该在 TCP 之上添加一个数据路由协议,并在一个接收线程中实现这个协议。该线程可以保留一个虚拟连接列表,从而为来自其他线程的流/消息请求提供服务。