每个客户端一个线程与线程服务器的排队线程模型之间的相对优点?

2024-05-23

假设我们正在构建一个线程服务器,旨在在具有四个核心的系统上运行。我能想到的两种线程管理方案是每个客户端连接一个线程和一个排队系统。

正如第一个系统的名称所暗示的那样,我们将为每个连接到服务器的客户端生成一个线程。假设一个线程始终专用于程序的主执行线程,那么我们将能够同时处理最多三个客户端,并且对于任何更多的并发客户端,我们都必须依赖操作系统的抢占式多任务处理功能来进行切换它们(或者在绿色线程的情况下是虚拟机)。

对于第二种方法,我们将创建两个线程安全队列。一种用于传入消息,一种用于传出消息。换句话说,请求和回复。这意味着我们可能会有一个线程接受传入连接并将其请求放入传入队列中。一两个线程将处理传入请求、解析适当的答复并将这些答复放置在传出队列上。最后,我们将有一个线程从该队列中取出回复并将其发送回客户端。

这些方法的优点和缺点是什么?请注意,我没有提到这是什么类型的服务器。我假设哪一个具有更好的性能配置文件取决于服务器是否处理短连接(如 Web 服务器和 POP3 服务器)或较长连接(如 WebSocket 服务器、游戏服务器和消息应用程序服务器)。

除了这两种之外还有其他的线程管理策略吗?


我相信我曾经做过这两个组织。


Method 1

就这样我们在同一页面上,第一个让主线程执行listen。然后,在一个循环中,它确实accept。然后它将返回值传递给pthread_create并且客户端线程的循环确实recv/send循环处理远程客户端想要的所有命令。完成后,它会清理并终止。

有关这方面的示例,请参阅我最近的回答:使用socket进行多线程文件传输 https://stackoverflow.com/questions/37497556/multi-threaded-file-transfer-with-socket/37506686#37506686

这样做的优点是主线程和客户端线程简单且独立。没有线程会等待另一个线程正在执行的任何操作。没有线程会等待任何不必要的事情。因此,客户端线程[多个]都可以以最大线速度运行。另外,如果客户端线程被阻塞recv or send,另一个线程可以走了,它会的。这是自我平衡。

所有线程循环都很简单:wait for input, process, send output, repeat。甚至主线程也很简单:sock = accept, pthread_create(sock), repeat

另一件事。客户端线程与其远程客户端之间的交互可以是anything他们同意。任何协议或任何类型的数据传输。


Method 2

这有点类似于 N 工作模型,其中 N 是固定的。

因为accept[通常]是阻塞的,我们需要一个与方法 1 类似的主线程。除了,它不需要启动一个新线程,而是需要 malloc 一个控制结构 [或其他一些管理方案] 并将套接字放入那。然后它将其放入客户端连接列表中,然后循环回accept

除了N个工作线程之外,你是对的。至少two控制线程,一件事情select/poll, recv, enqueue request和一件要做的事wait for result, select/poll, send.

需要两个线程来防止其中一个线程必须等待两个不同的事物:各种套接字[作为一个组]以及来自各个工作线程的请求/结果队列。对于单个控制线程,所有操作都必须是non- 阻塞,线程会疯狂旋转。

这是线程的[极其]简化版本:

// control thread for recv:
while (1) {
    // (1) do blocking poll on all client connection sockets for read
    poll(...)

    // (2) for all pending sockets do a recv for a request block and enqueue
    //     it on the request queue
    for (all in read_mask) {
        request_buf = dequeue(control_free_list)
        recv(request_buf);
        enqueue(request_list,request_buf);
    }
}

// control thread for recv:
while (1) {
    // (1) do blocking wait on result queue

    // (2) peek at all result queue elements and create aggregate write mask
    //     for poll from the socket numbers

    // (3) do blocking poll on all client connection sockets for write
    poll(...)

    // (4) for all pending sockets that can be written to
    for (all in write_mask) {
        // find and dequeue first result buffer from result queue that
        // matches the given client
        result_buf = dequeue(result_list,client_id);
        send(request_buf);
        enqueue(control_free_list,request_buf);
    }
}

// worker thread:
while (1) {
    // (1) do blocking wait on request queue
    request_buf = dequeue(request_list);

    // (2) process request ...

    // (3) do blocking poll on all client connection sockets for write
    enqueue(result_list,request_buf);
}

现在,有几点需要注意。仅有的one请求队列用于所有工作线程。这recv控制线程做了not尝试选择一个空闲[或未充分利用]的工作线程并排队到特定于线程的队列[这是要考虑的另一个选项]。

单个请求队列可能是最有效的。但是,也许并非所有工作线程都是平等创建的。有些可能最终出现在具有特殊加速硬件的 CPU 核心 [或集群节点] 上,因此某些请求可能会have发送到特定线程。

而且,如果这样做了,线程可以“窃取工作”吗?也就是说,一个线程完成其所有工作,并注意到另一个线程在其队列中有一个请求[兼容]但尚未启动。线程使请求出队并开始处理它。

这种方法有一个很大的缺点。请求/结果块[大部分]具有固定大小。我已经完成了一个实现,其中控件可以有一个用于“侧面/额外”有效负载指针的字段,该指针可以是任意大小。

但是,如果进行大型传输文件传输(无论是上传还是下载),尝试通过请求块传递这些零碎的文件并不是一个好主意。

在下载情况下,工作线程可能会暂时占用套接字并send在将结果排队到控制线程之前先读取文件数据。

但是,对于上传情况,如果工作人员尝试在紧密循环中进行上传,则会与recv控制线程。工作人员必须[以某种方式]提醒控制线程not将套接字包含在其轮询掩码中。

这开始变得复杂。

而且,所有这些请求/结果块入队/出队都会产生开销。

此外,两个控制线程也是一个“热点”。系统的整个吞吐量取决于它们。

并且,套接字之间存在交互。在简单的情况下,recv线程可以在一个套接字上一对一地启动,但其他希望发送请求的客户端会被延迟,直到recv完成。这是一个瓶颈。

这意味着所有recv系统调用必须是非阻塞的[异步]。控制线程必须管理这些异步请求(即启动一个请求并等待异步完成通知,以及only然后将请求放入请求队列)。

这开始变得复杂。

这样做的主要好处是拥有大量并发客户端(例如 50,000 个),但将线程数量保持在合理的值(例如 100)。

该方法的另一个优点是可以分配优先级并使用多个优先级队列


比较和混合

同时,方法 1 执行方法 2 执行的所有操作,但以更简单、更稳健的方式 [并且我怀疑,吞吐量更高]。

创建方法 1 客户端线程后,它可能会拆分工作并创建多个子线程。然后,它可以像方法 2 的控制线程一样运行。事实上,它可以像方法 2 一样从固定的 N 池中利用这些线程。

这将弥补方法 1 的弱点,即线程将执行大量计算。如果有大量线程都在进行计算,系统就会陷入困境。排队方法有助于缓解这种情况。客户端线程仍然被创建/活动,但它在结果队列上休眠。

所以,我们只是把水搅得更浑了。

任何一种方法都可以是“正面”方法,并且在下面具有另一种方法的元素。

给定的客户端线程[方法 1] 或工作线程[方法 2] 可以通过打开与“后台”计算集群的另一个连接来分担其工作。可以使用任一方法来管理集群。

因此,方法 1 更简单、更容易实施,并且可以轻松适应大多数作业组合。对于重型计算服务器来说,方法 2 可能更好,可以限制对有限资源的请求。但是,必须谨慎使用方法 2,以避免出现瓶颈。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

每个客户端一个线程与线程服务器的排队线程模型之间的相对优点? 的相关文章

  • 具有阻塞功能的 Twisted LoopingCall

    我有一个应用程序需要轮询数据库以了解可能的配置更改 该应用程序是一个使用 Twisted 的简单 xmlrpc 服务器 我尝试过使用Twisted的LoopingCall来执行轮询 但是由于LoopingCall在主线程上运行 所以对db的
  • Android SurfaceView 使用线程绘制画布

    我正在尝试使用线程在画布上绘图来创建一个简单的游戏引擎 但我遇到了一些无法解释的奇怪问题 这个 游戏 的目的是每秒在画布上画一个圆圈 这是可行的 但不是我想要的工作方式 似乎应用程序正在两个画布之间切换 并向每个画布添加一个圆圈 这样您就可
  • ThreadPoolExecutor 和队列

    我以为使用线程池执行器 http docs oracle com javase 6 docs api java util concurrent ThreadPoolExecutor html我们可以提交Runnables 要在以下位置执行B
  • 如何在线程创建和退出时调用函数?

    include
  • 使用 Matplotlib、PyQt 和 Threading 进行实时绘图导致 python 崩溃

    我一直在努力研究我的 Python 应用程序 但找不到任何答案 我有 PyQT GUI 应用程序 它使用 Matplotlib 小部件 GUI 启动一个新线程来处理 mpl 小部件的绘图 恐怕我现在通过从另一个线程访问 matplotlib
  • 如何在给定点停止线程?

    我试图停止一些线程 阅读一些有关优雅地执行此操作的正确方法的内容 但我一定做错了什么 因为它根本不起作用 起初我尝试不使用lock with IsRunning不稳定 然后尝试使用锁 这是我所拥有的 private volatile boo
  • Hazelcast 分布式锁与 iMap

    我们目前使用 Hazelcast 3 1 5 我有一个简单的分布式锁定机制 应该可以跨多个 JVM 节点提供线程安全性 代码非常简单 private static HazelcastInstance hInst getHazelcastIn
  • 我们可以使用 x86_64 CPU 原子在 PCI Express 上生成复合原子操作吗?

    如您所知 从2 0版本开始 PCI Express支持复合原子操作 FetchAdd Swap CAS https pcisig com sites default files specification documents ECN Ato
  • ASP.NET 开发人员真的需要关心线程安全吗?

    我认为自己了解线程的概念以及为什么某些代码是或不是 线程安全 的 但作为主要使用 ASP NET 的人 线程和线程安全是我很少考虑的事情 然而 我似乎在 Stack Overflow 上遇到了大量评论和答案 不一定适用于 ASP NET 大
  • IO 密集型任务中的 Python 多线程

    建议仅在 IO 密集型任务中使用 Python 多线程 因为 Python 有一个全局解释器锁 GIL 只允许一个线程持有 Python 解释器的控制权 然而 多线程对于 IO 密集型操作有意义吗 https stackoverflow c
  • 如何延迟 onClick 操作

    我正在尝试在 java 应用程序 android 中做一些事情 并且我需要一些东西来延迟 等待循环的秒数 我怎样才能延迟android功能 我尝试过使用 Thread sleep TimeUnit sleep 但它只会执行几秒钟的不负责任的
  • 如何处理 MSTest 中的 currentDomain.UnhandledException

    我尝试根据答案实施解决方案单元测试时如何处理其他线程引发的异常 https stackoverflow com a 934604 518530 但我仍然不明白在处理程序中要做什么 假设我有一个测试 TestMethod void Test
  • Netty UDP 服务器中的线程未同时执行

    我正在分析的代码使用 Netty NioDatagramChannelFactory 创建 UDP 服务器 它创建一个线程池 ExecutorService threadPool Executors newCachedThreadPool
  • 从子线程绘制到窗口

    我的应用程序从工作线程绘制图形已有 10 多年了 而且我从未遇到过任何问题 工作线程吸引到我的HWND 由主线程创建 如下所示 hdc GetDC hwnd SetDIBitsToDevice or StretchDIBits Releas
  • 如何使用单独的线程部分重绘自定义 SurfaceView 而不会丢失以前的编辑?

    我目前在按照自定义绘图时遇到问题SurfaceView来自不在我的主 UI 上的线程 这SurfaceView占据整个屏幕 Galaxy S3 全屏 并且必须从多个来源进行更新 问题是习惯SurfaceView不会保存 UI 更新之间的更改
  • ActionScript Workers 可以用于在单独的线程中播放/生成声音吗?

    我很新ActionScript 工作者 但我想知道这是否可能 从我读到的来看 ActionScript 工作者 ASW 就像单独的线程 可以执行更多 CPU 密集型计算 而无需中断主线程 正在执行主 SWF 文件 我真正看到的唯一一个例子是
  • 无法访问类型的封闭实例。 [复制]

    这个问题在这里已经有答案了 整个代码是 public class ThreadLocalTest ThreadLocal
  • JavaFX Platform.runLater 的使用以及从不同线程访问 UI

    我有几个问题Platform runLater 我有一个 JavaFX 应用程序类 在这个类中 我运行一个线程 该线程从网络套接字读取数据 现在当我创建一个新的Stage在线程内部 系统抛出异常 JavaFX 事件调度程序线程和我的网络读取
  • Java:使用 Java.util.concurrent 线程访问读取线程串行端口

    我正在尝试编写一个 Java 串行设备驱动程序并想使用 对我来说是新的 java util concurrent包裹 我有一种发送数据包然后等待 ACK 的方法 我打算有炭 接收在不同的线程中运行 如果接收线程收到 ACK 它应该使用发送数
  • QThread - 使用槽 quit() 退出线程

    我想在线程完成运行时通知对象 但是 我无法让线程正确退出 我有以下代码 处理器 cpp thread new QThread tw new ThreadWorker connect tw SIGNAL updateStatus QStrin

随机推荐

  • 禁用 Android 菜单组

    我尝试使用以下代码禁用菜单组 但它不起作用 菜单项仍然启用 你能告诉我出了什么问题吗 资源 菜单 menu xml menu menu
  • 在 Oracle 中如何将多行组合成逗号分隔的列表? [复制]

    这个问题在这里已经有答案了 我有一个简单的查询 select from countries 结果如下 country name Albania Andorra Antigua 我想在一行中返回结果 如下所示 Albania Andorra
  • 如何在亚马逊 EC2 上调试 python 网站?

    我是网络开发新手 这可能是一个愚蠢的问题 但我找不到可以帮助我的确切答案或教程 我工作的公司的网站 用 python django 构建 托管在亚马逊 EC2 上 我想知道从哪里开始调试这个生产站点并检查存储在那里的日志和数据库 我有帐户信
  • 悬停时的 CSS 过渡

    我有个问题 实际上 当我将鼠标悬停在对象上时 我尝试在 div 上进行转换 所以基本上我有一个div 当我将鼠标悬停在div上时 它应该在其顶部显示另一个div 但是它应该被转换 这样悬停效果会更平滑 如果我有这两个 div 怎么可能 di
  • parent_id 是外键(自引用)并且为 null?

    浏览 Bill Karwin 的书 SQL Antipatterns 第 3 章 Naive Trees 邻接表 父子关系 有一个注释表的示例 CREATE TABLE Comments comment id SERIAL PRIMARY
  • ggplot2以限制为中心的多边形世界地图给出了有趣的边缘

    使用下面的代码我生成了一张以华盛顿特区为中心的地图 解决方案基于科斯克的解决方案在这里 https stackoverflow com questions 10620862 use different center than the pri
  • 伪元素的元素类型是什么?

  • “条件长度 > 1 并且仅使用第一个元素”错误

    我对 f 语句有疑问 因为它返回给我以下错误消息 条件长度 gt 1 并且仅使用第一个元素 我有一个名为 data summary 的数据框 我想创建两个新变量vol up and vol down取决于我的数据框的其他变量 这是我的脚本代
  • 如果不是,则必须删除单元格的第一个字符 #3Created 循环永远不会结束

    所以基本上 我需要删除主键字段中第二位数字不为 3 的所有记录 例如可以如下所示 39001 或者没有 3 我想要的是所有以非 3 开头的单元格 它们的行都被删除我想出了以下代码 它删除了所有单元格 但宏永远不会停止运行 Sub keep3
  • AWS Lambda 不读取环境变量

    我正在编写一个 python 脚本来查询 Qualys API 中的漏洞元数据 我在 AWS 中将其作为 lambda 函数执行 我已经在控制台中设置了环境变量 但是当我执行函数时 出现以下错误 module initialization
  • PhpStorm背景错误

    PhpStorm更新后 Blade模板中 script标签突出显示 在设置中 一切正常 为什么要突出显示这一点 检查语言注入中是否有非 内置 行 禁用您不认识的项目
  • 如何在复杂的皂膜GAM中设置更平滑的边界条件?

    我正在对南太平洋岛屿泻湖中宽吻海豚的分布进行建模 我想使用肥皂膜平滑器来模拟海豚在二维表面 经度 x 纬度 上存在的概率 考虑到陆地边界 显然海豚不能在陆地上行走 我想知道如何将我的研究区域 陆地和近海水域 的边界固定为等于零的条件 因为我
  • 如何使用 fetch() 和 WhatWG 流获取文件上传进度

    注意 我并不是在寻找任何替代方案 我知道这可以通过 XMLHttpRequest 来完成 我也不关心浏览器支持 我只想了解新的 即将推出的标准 我有一个File https developer mozilla org en US docs
  • PHP Json_encode 将空格更改为加号 +

    我有一个网络应用程序 我首先将 JSON 数据存储在 cookie 中 然后每 x 秒保存到数据库 它只是打开与服务器的连接 服务器读取 cookie 它实际上并不通过 POST 或 GET 发送任何内容 当我保存到 cookie 时 我的
  • 如何检测脚本是否正在被获取

    我有一个脚本 我不希望它调用exit如果它正在被采购 我想检查一下是否 0 bash但是如果脚本源自另一个脚本 或者用户从不同的 shell 获取它 例如ksh 有没有可靠的方法来检测脚本是否来源 稳健的解决方案bash ksh zsh 包
  • sqlite3和pdo_sqlite有什么区别

    我正在将我的 Web 应用程序从 MySQL 迁移到 SQLite 数据库 我发现有两个 PHP 扩展用于与 sqlite 通信 php sqlite3 dll and php pdo sqlite dll 什么扩展比较好 或者另一个问题
  • 表单 CSS:根据选中/未选中状态设置单选框的父级(标签)样式

    所以我有一个表格 表格中提出的大多数问题都是使用无线电输入 我要和
  • 如何在C#中控制datagridview光标移动

    我希望 datagridview 光标向右移动到下一列 而不是在向单元格输入数据后移动到下一行 我试图通过 dataGridView1 KeyDown 事件捕获键来控制光标 但这并不能阻止光标在将数据输入到单元格后移动到下一行 提前感谢你的
  • 如何将带有参数的Python装饰器实现为类?

    我正在尝试实现一个接受一些参数的装饰器 通常带有参数的装饰器被实现为双重嵌套闭包 如下所示 def mydecorator param1 param2 do something with params def wrapper fn def
  • 每个客户端一个线程与线程服务器的排队线程模型之间的相对优点?

    假设我们正在构建一个线程服务器 旨在在具有四个核心的系统上运行 我能想到的两种线程管理方案是每个客户端连接一个线程和一个排队系统 正如第一个系统的名称所暗示的那样 我们将为每个连接到服务器的客户端生成一个线程 假设一个线程始终专用于程序的主