两个线程可以同时与同一端口上的两个不同套接字进行交互吗?

时间:2016-06-29 22:49:11

标签: multithreading sockets io operating-system port

假设我有一个多线程服务器将数据写入同一端口上的两个不同套接字,其中专用线程处理每个套接字。两个线程是否可以同时写入各自的套接字? ("同时",我的意思是真正的同时性,而不仅仅是并发交错。)或者套接字是否共享相同的端口意味着实施互斥?

总的来说,我不清楚两个任意I / O流之间如何共享资源。我知道两个线程不能同时写入磁盘,因为磁盘本身是一个共享资源。然而,在插座和端口的情况下,我没有类似的物理模型来指导我的推理。一个相关的问题是不同类型的I / O流之间是否存在共享资源 - 例如,两个线程写入两个文件描述符之间是否存在争用,一个用于网络套接字,另一个用于磁盘上的文件?

1 个答案:

答案 0 :(得分:1)

虽然强制执行互斥,但这并不过分有用......让我解释一下。

来自linux man read(3)

  

I / O旨在成为普通文件和管道以及FIFO的原子。原子意味着来自单个操作的所有字节一起开始一起结束,而不与其他I / O操作交错。终端的已知属性不受尊重,并且终端明确地(并且隐式地永久地)排除,使得行为未被指定。其他设备类型的行为也未指定,但措辞旨在暗示未来的标准可能会选择指定原子性(或不是)。

套接字计为FIFO。

此外,套接字共享相同的底层硬件,因此同一个以太网接口上的所有套接字都必须同步才能传递到实际的网络(即使在不同的套接字中也会发生同步)。

因此,从技术上讲,您可以从两个或多个线程(甚至进程)编写而不会破坏IO层并导致一堆乱七八糟的东西。

然而,您应该注意到并非所有保证写入fd的字节。正如写作中提到的那样:

  

write()将缓冲区指向的buf中的最多计数字节写入文件描述符fd引用的文件。

当两个(或多个)进程/线程尝试写入同一个套接字时,这可能会导致碎片。

例如,假设以下情形:

Thread1 calls `write(fd, "Hello long message", 18);`

Context switch.

Thread2 calls `write(fd, "Hello another message", 20);`

Context switch.

Thread1 gets the return value of 7 (there wasn't enough room in the buffer for the long message), meaning only some off the message was sent.

Context switch (system does some stuff too).

Thread1 gets the return value of 10 (there wasn't enough room in the buffer for the long message), meaning only some off the message was sent.

在这种情况下,客户端得到类似"Hello lHello anot"(或者"Hello anotHello l"的内容,因为write操作的顺序在这种情况下可能不受保证)预期的效果......

即使操作系统保证readwrite都可以被视为" atomic" (我们没有得到"HelHellolo..."),你仍然会发现自己需要一个用户缓冲区和某种同步器来管理并行写入。

如果你感到懒惰,我为two file library for common multithreaded sock operations写了一个小my project。我不认为我已经测试了两个并行线程写入,但是我将实现HTTP / 2。

这个库不是我能想到的性能最强的代码,但它可能会给你一个开始的地方。

P.S。

从您的问题来看,您似乎正在编写一个服务器应用程序,每个客户端使用一个帖子......如果是这样,我建议您重新考虑。

运行此类服务器应用程序的任何计算机的负载过重都会导致整个计算机崩溃,因为DoS攻击会导致产生足够多的线程。机器开始执行更多上下文切换然后执行任务,直到它什么都不做(除了可能烧掉CPU)之前只进行上下文切换。

编辑(回答评论中的问题)

基本上,在底层实现中,每个fd被分配一个" lock" (我简化了,但它基本上是如何完成的。)

readwrite,任何被认为是原子的I / O操作都会在执行任何操作之前尝试获取锁。

这意味着这些操作仅针对相同的fd是原子的。因此,影响相同fd的任何两个I / O操作将彼此同步并给人以原子的印象。

在较低级别,由于套接字共享相同的硬件(假设您有一个网络接口),tcp数据包会在发送时进行同步,因此不会发生tcp数据包碎片。

然而,这是与I / O C API不同的同步问题,因为OS(C API)提供的I / O写入中间内部缓冲区。

内核在写入文件,套接字,管道等时管理此缓冲区。 - 每个都有自己独特的同步问题..即非SSD硬盘需要将其写入与磁盘轮换同步,并且无法真正提供良好的硬件并发性。

内核问题,硬件问题和API约束都是不同的级别,并发性将会丢失或实现。一些失去并发性的操作通过硬件加速获得性能......

...最后,作为软件开发人员,我们最终会尽力而为,并希望内核和硬件尽最大努力。