我试图在脑海中找出构建 Cocoa 应用程序的最佳方法,该应用程序本质上是一个并发下载管理器。应用程序与服务器进行通信,用户列出一个要下拉的大列表,然后应用程序处理该列表。(它不使用 HTTP 或 FTP,所以我无法使用 URL 加载系统;我将讨论套接字连接。)

这基本上是经典的生产者-消费者模式。诀窍在于消费者的数量是固定的,而且他们是持久的。服务器对可以打开的同时连接数设置了严格的限制(尽管通常至少有两个),并且打开新连接的成本很高,因此在理想情况下,在应用程序的生命周期中打开相同的 N 个连接。

解决这个问题的一种方法可能是创建 N 个线程,每个线程“拥有”一个连接,并等待请求队列,如果队列为空则阻塞。由于连接数量永远不会很大,因此就实际系统开销而言,这并非不合理。但从概念上讲,Cocoa 似乎必须提供一个更优雅的解决方案。

看来我可以使用 NSOperationQueue, ,并致电 setMaxConcurrentOperationCount: 与连接数。然后我将下载请求放入该队列中。但我不确定在这种情况下如何管理连接本身。(只需将它们放在堆栈上,并依靠队列来确保我不会运行过度/不足?扔进一个 调度信号量 与堆栈一起?)

现在我们正处于勇敢的新世界 大中央快讯, ,这是否开辟了解决这个问题的其他方法?乍一看,似乎并非如此,因为 GCD 的旗舰能力是动态扩展并发(并且在 Apple 的建议中提到过) 改变生产者-消费者实施)实际上并没有帮助我。但我刚刚读到了它的皮毛。

编辑:

如果重要的话:是的,我计划使用异步/非阻塞套接字 API 来与服务器进行实际通信。因此 I/O 本身不必位于其自己的线程上。我只关心对工作进行排队的机制,并在连接可用时(安全地)将其分发给连接。

有帮助吗?

解决方案 2

为了后代的缘故,经过其他地方的一些讨论,我认为我会采用的解决方案基本上是:

  • 有一个等待下载操作的队列,最初是空的。
  • 有一个包含所有打开连接的集合,最初是空的。
  • 有一个空闲打开连接的可变数组(实际上是队列),最初是空的。
  • 当用户添加下载请求时:
    • 如果空闲连接数组不为空,则删除一个并将下载分配给它。
    • 如果没有空闲连接,但总连接数未达到限制,则打开一个新连接,将其添加到集合中,并将下载分配给它。
    • 否则,请将下载排入队列以供稍后使用。
  • 下载完成后:如果有排队的请求,请删除一个并将其交给连接;否则,将该连接添加到空闲列表中。

所有这些工作都将在主线程上进行。解码每次下载结果的工作将被卸载到 GCD,因此它可以处理并发限制,并且不会阻塞主线程。

打开新连接可能需要一段时间,因此在实际实践中创建新连接的过程可能会更复杂一些(例如,将下载入队,启动连接过程,然后在连接完全建立时将其出队)。但我仍然认为我对竞争条件可能性的看法被夸大了。

其他提示

如果您正在使用CFSocket的非阻塞调用的I / O,我同意,应该一切发生在主线程,让操作系统处理并发问题,因为你只是复制数据,并没有真正做任何计算

除此之外,这听起来像您的应用程序需要做的唯一的其他工作是维护项目的队列中下载。当传输中的任何一个完成时,CFSocket回调可以启动下一个项目的队列传送。 (如果队列为空,你递减连接计数,如果事情被添加到空队列,开始新的传输。)我不明白为什么你需要多个线程这一点。

也许你忽略了一些重要的东西,但根据你的描述的应用程序是I / O密集型,而不是CPU的约束,所以所有的并发性的东西仅仅是将使用更复杂的代码对性能的影响最小。

做这一切的主线程上。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top