【发布时间】:2023-03-15 06:51:02
【问题描述】:
我正在编写一个生成、缓存和提供 zip 文件的 Web 服务。
如果缓存中不存在请求的文件,则会生成该文件,然后提供该文件。根据请求,生成此文件可能需要相当长的时间。同一 zip 文件的另一个请求可能会进入,因为它仍在第一个请求中生成。
一个基本的场景可能是这样的
- 线程 1:给我 bigfile.zip
- 线程 1:bigfile.zip 不存在
- 线程 1:生成 bigfile.zip
- 线程 2:给我 bigfile.zip
- 线程 2:线程 1 正在生成 bigfile.zip - 等待它完成
- 线程1:完成生成bigfile.zip
- 线程 1:服务 bigfile.zip
- 线程 2:服务 bigfile.zip
所以我正在考虑使用Thread 来实现这一点,并在文件准备好后使用Join() 来同步它们。
但是我有一个问题。我将如何管理多个不同文件的多个请求?我正在考虑使用Dictionary<fileId, Thread> 来跟踪它们,但是当它完成它的过程时,我怎样才能安全地从字典中删除一个线程呢?如果不对整个事情进行锁定,我看不到任何方法 - 包括实际过程本身。当然,这样做似乎首先会使线程的整个想法变得多余。
lock(_myLocker)
{
if(!fileThreads.containsKey(fileId))
{
Thread myThread = MakeMeAThread();
fileThreads.add(fileId, myThread);
}
fileThreads[fileId].Join();
//We have to do the Join inside the lock, this is the only way we know (in a threadsafe manner) that the dictionary definitely contains our key
}
ServeTheFile();
//How do I clean up the no longer required fileThreads[fileId]?
为了增加难度,还有另一种使用服务的方式,它简单地告诉客户端正在请求的文件的状态(不可用 (404)、正在生成、准备好)。
- 线程 1:给我 bigfile.zip
- 线程 1:bigfile.zip 不存在
- 线程 1:生成 bigfile.zip
- 线程 2:给我 bigfile.zip
- 线程 2:线程 1 正在生成 bigfile.zip - 等待它完成
- 线程 3:你有 bigfile.zip 吗? - 不,它正在生成
- 线程1:完成生成bigfile.zip
- 线程 1:服务 bigfile.zip
- 线程 2:服务 bigfile.zip
- 线程 4:你有 bigfile.zip 吗?是的,它已经为您准备好了
- 线程 5:你有 invalid.zip 吗?不,这是一个无效请求
那么,您知道为什么我们不能只锁定流程吗?如果我们这样做了,线程 3 就无法被告知文件正在生成,并且必须等待文件生成完成。
【问题讨论】:
-
为什么不把
Join()语句移到锁外呢? -
完成后使线程从字典中删除。更好的是,使用任务。这样你就可以附加一个延续。
-
写完我的答案(见下文)后,我刚刚重新阅读了你的整个问题。您的最后一句话指出,您避免了锁定,因为如果当前生成了请求的文件,则可能存在等待。我对此的问题是:在上述情况下应该发生什么?您是否希望线程将正在生成文件的信息再次返回给客户端,并且客户端必须轮询直到文件可用?
-
所以不要使用
lock,而是使用Monitor.TryEnter等。如果你在ASP.NET中这样做,那么生成和缓存文件的东西可能应该是Windows服务而不是ASP.NET 上下文中的一个线程。 -
@AcidJunkie,是的,客户端必须轮询,直到文件可用。由于它是一个 Web 应用程序,默认方法只是请求文件并等待。但是当我们添加 javascript 时,我们可以使用 ajax 来轮询文件的可用性。
标签: c# .net multithreading threadpool