首页 文章

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

提问于
浏览
0

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

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

1 回答

  • 1

    虽然强制执行互斥,但这并没有太大帮助......让我解释一下 .

    来自linux man read(3)

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

    套接字计为FIFO .

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

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

    However ,您应该注意到并非所有保证要写入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..." ),您仍然会发现自己需要用户驻地缓冲区和某种同步器来管理并行写入 .

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

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

    附:

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

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

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

    基本上,在底层实现中,每个 fd 都被分配了"lock"(基本上我是如何完成的) .

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

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

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

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

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

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

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

相关问题