假设我有一个多线程服务器将数据写入同一端口上的两个不同的套接字,其中一个专用线程处理每个套接字 . 两个线程是否可以同时写入各自的套接字? (通过“同时”,我的意思是真正的同时性,而不仅仅是并发交错 . )或者套接字是否共享相同的端口意味着相互排斥是强制执行的?
一般来说,我不清楚两个任意I / O流之间如何共享资源 . 我知道两个线程不能同时写入磁盘,因为磁盘本身是一个共享资源 . 然而,在套接字和端口的情况下,我没有类似的物理模型来指导我的推理 . 一个相关的问题是不同类型的I / O流之间是否存在共享资源 - 例如,两个线程写入两个文件描述符之间是否存在争用,一个用于网络套接字,另一个用于磁盘上的文件?
1 回答
虽然强制执行互斥,但这并没有太大帮助......让我解释一下 .
来自linux man read(3):
套接字计为FIFO .
此外,套接字共享相同的底层硬件,因此同一个以太网接口上的所有套接字都必须同步才能传递到实际的网络(即使在不同的套接字中也会发生同步) .
因此,从技术上讲,您可以从两个或多个线程(甚至进程)进行编写,而不会破坏IO层并导致一堆乱七八糟的东西 .
However ,您应该注意到并非所有保证要写入fd的字节 . 正如写作中提到的那样:
当两个(或多个)进程/线程尝试写入同一个套接字时,这可能会导致碎片 .
例如,假设以下情形:
在这种情况下,客户端得到类似
"Hello lHello anot"
(或者"Hello anotHello l"
,因为write
操作的顺序可能在这种情况下不保证),这不是预期的效果......因此,即使操作系统保证
read
和write
都可以被视为"atomic"(我们没有得到"HelHellolo..."
),您仍然会发现自己需要用户驻地缓冲区和某种同步器来管理并行写入 .如果你感到懒惰,我为my project写了一个小two file library for common multithreaded sock operations . 我不认为我测试了两个并行线程写入,但我将实现HTTP / 2 .
这个库不是我能想到的最具性能导向的代码,但它可能会给你一个开始的地方 .
附:
从您的问题看来,您似乎正在编写一个服务器应用程序,每个客户端使用一个线程......如果是这种情况,我建议您重新考虑 .
运行此类服务器应用程序的任何计算机的重负载都会导致整个计算机崩溃,因为DoS攻击会导致产生足够多的线程 . 机器被卡住执行更多上下文切换然后执行任务,直到它只是上下文切换而什么都不做(除了,可能,烧掉CPU) .
编辑(回答评论中的问题)
基本上,在底层实现中,每个
fd
都被分配了"lock"(基本上我是如何完成的) .read
,write
并且任何被认为是原子的I / O操作都会在执行任何操作之前尝试获取锁 .这意味着这些操作仅对于相同的
fd
是原子的 . 因此,影响相同fd
的任何两个I / O操作将彼此同步并给人以原子的印象 .在较低级别,由于套接字共享相同的硬件(假设您有一个网络接口),tcp数据包在发送时会同步,因此没有tcp数据包碎片将发生 .
然而,这是与I / O C API不同的同步问题,因为OS(C API)提供的I / O写入中间内部缓冲区 .
内核在写入文件,套接字,管道等时管理这个缓冲区 - 每个缓冲区都有自己独特的同步问题 . 即非SSD硬盘需要将其写入与磁盘轮换同步,并且无法真正提供良好的硬件并发性 .
内核问题,硬件问题和API约束都是不同的级别,其中并发性将丢失或实现 . 一些失去并发性的操作通过硬件加速获得性能......
...最后,作为软件开发人员,我们最终会尽力而为,并希望内核和硬件尽最大努力 .