【问题标题】:More appropriate for my task: background worker or thread pool?更适合我的任务:后台工作者还是线程池?
【发布时间】:2026-01-12 06:05:01
【问题描述】:

我有一个简单的 Web 应用程序模块,它基本上接受来自移动客户端应用程序的在 PageLoad 上保存 zip 文件的请求。

现在,我要做的是解压缩文件并读取其中的文件并进一步处理它..包括将条目输入数据库。

更新: zip 文件及其内容的大小将相当小,因此服务器不应承受太多负载。

更新 2: 我刚刚读到了 when IIS queues requests(在全局/应用级别)。那么这是否意味着我不需要实现复杂的请求处理机制,而 IIS 可以自己处理应用程序?

更新 3: 我正在寻找卸载下载 zip 的处理,不仅是为了最大限度地减少开销(在性能方面),而且也是为了避免 @987654323 的问题@ 当文件被处理并将记录更新到同一个表中时。在多个设备请求页面和后台任务处理数据库并行更新的场景下会导致异常。

到目前为止,我已经确定了两种解决方案:

  • 实现并发/消息队列
  • 将文件处理代码实现到一个单独的工具中,并在服务器上安排一个作业来检查未处理的文件并按顺序处理它们。

倾向于Queuing Mechanism 我将尝试实现它,因为它似乎不太依赖于配置。 v/s 在服务器端手动配置作业/计划。

那么,你们为此推荐我什么?

此外,在请求 zip 文件并将其保存在服务器端后,客户端和服务器端的连接会在此之后释放。不想给我的 IIS 增加负担。

假设有几百个客户端同时请求该页面..

实际上我之前都没有使用过它们,所以任何示例或操作方法都会更受欢迎。

【问题讨论】:

  • 如果您的服务器在处理过程中崩溃了怎么办?它应该 (i) 在重新启动时恢复处理还是 (ii) 丢失任何排队或正在进行的工作?
  • @IanMercer 那将是 (i)。保存 zip 文件后,它应该解压缩内容..开始阅读文件..使条目符合要求。到文件。 一次性 这就是我所追求的。丢失正在排队的数据/任务不是一种选择。 :|
  • 没有无限数量的无限快核心,这是不可能的。如果您想接近不丢失数据的目标,您需要将请求排入持久队列(数据库或数据库支持的队列)并在单独的进程中处理。

标签: c# asp.net iis backgroundworker threadpool


【解决方案1】:

我建议使用排队系统。

收到文件后,您会将路径保存到线程同步队列中。同时,后台工作人员(或者最好是另一台机器)将检查此队列中的新文件并将条目出列以处理它。

这样您就不会启动未知数量的线程(每个 zip 文件),并且可以在一个位置处理 zip 文件。这样,当负载过重时,您还可以更轻松地将邮政编码处理代码移动到另一台机器上。您只需要访问一个公共队列。

最简单的方法可能是将静态Queuelock 对象一起使用。它是最容易实现的,不需要外部资源。但这会导致您的应用程序回收时队列丢失。

您提到丢失 zip 文件不是一种选择,如果您不想依赖外部资源,那么这种方法不是最好的。根据您的负载,可能值得利用外部资源 - 这意味着将 zip 文件上传到另一台机器上的公共存储,并将消息添加到另一台机器上的队列中。

这是一个本地队列的示例:

ConcurrentQueue<string> queue = new ConcurrentQueue<string>();

void GotNewZip(string pathToZip)
{
    queue.Enqueue(pathToZip); // Added a new work item to the queue
}

void MethodCalledByWorker()
{
    while (true)
    {
        if (queue.IsEmpty)
        {
            // Supposedly no work to be done, wait a few seconds and check again (new iteration)
            Thread.Sleep(TimeSpan.FromSeconds(5));
            continue;
        }

        string pathToZip;
        if (queue.TryDequeue(out pathToZip)) // If TryDeqeue returns false, another thread dequeue the last element already
        {
            HandleZipFile(pathToZip);
        }
    }
}

这是一个非常粗略的例子。每当一个 zip 到达时,您就将路径添加到队列中。同时,后台工作人员(或多个,示例的线程安全)将处理一个接一个的 zip,从队列中获取路径。 zip 文件将按照到达的顺序进行处理。

您需要确保您的应用程序不会同时回收。但是你在本地机器上拥有的所有资源都是这种情况,当你的机器崩溃时它们会丢失。

【讨论】:

  • 您好,感谢您的快速回复:) 但是如何将文件的路径发送到消息队列?这也必须使用线程执行吗?样本可以帮助/我可以查看的任何链接? +1 不错的方法
  • 这取决于您使用哪种队列。最简单的可能是使用 .NET 的 Queue-class 和 lock-object。更“专业”(在你的情况下可能会过度杀伤,我不知道)将是另一台机器上的外部队列系统。访问这些队列当然取决于它们的库。理论上您也可以使用数据库,但我不知道您将如何在那里同步您的请求以使其成为线程安全的。
  • 我正在尽量减少开销,所以真的不会采用多层方法。 **更新:** 肯定不会有超过 9xx 设备尝试请求页面,所以.. :|请我在上面的评论#1(根据我的问题)。
  • 我想本地队列仍然是一种选择。只需将工作人员设置为几乎不睡觉,即使队列为空。您仍然需要确保自己的机器不会回收。但是使用这种方法(而不是在 zip 到达时立即开始新任务),当机器突然断开连接时,文件丢失会更少,因为一个文件将一个接一个地完成。
  • 顺便说一句,你知道 .NET 4 有 ConcurrentQueue?
【解决方案2】:

我推荐 TPL 和 Rx 扩展:将解压缩的文件列表设置为可观察的集合,并为每个项目异步启动一个新任务。

【讨论】:

  • 嗨 :) 但是开始一个新的async task 意味着开始一个新的进程。因此,假设我有 50 个客户端同时请求我的页面 == 50 个保存文件的请求 == 50 个不同的异步任务进程。
  • @codebreaker:理论上每个任务都会是一个新线程,但实际上所有任务都会发生在线程池上,无需您手动控制。 TPL/Rx 足够聪明!
  • 我同意这种方法,使用 Rx,您可以使用简单、流畅的 API 执行各种操作,例如重试。 Pluralsight 有一个GREAT Rx 课程:.NET Reactive Extensions Fundamentals,或者如果您没有 sub.,请查看 Paul Betts 的 this talk
【解决方案3】:

我相信您过早地进行了优化。

您提到了表锁定-您使用的是哪种数据库?如果您添加新行或更新现有行,大多数配置中的大多数现代数据库将:

  1. 使用行级锁定;和
  2. 足够快,无需担心 锁定。

我建议从一个简单的方法开始

        //Unzip
        //Do work
        //Save results to database

并获得一些证明它太慢了。

【讨论】: