Cocoa中的并发网络客户端

时间:2009-09-19 22:27:55

标签: cocoa architecture networking concurrency grand-central-dispatch

我正在努力解决构建Cocoa应用程序的最佳方法,该应用程序本质上是一个并发下载管理器。有一个应用程序与之交谈的服务器,用户会列出要下拉的内容,以及应用程序处理列表。 (它不使用HTTP或FTP,所以我不能使用URL加载系统;我将讨论套接字连接。)

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

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

似乎我可以使用NSOperationQueue,并使用连接数调用setMaxConcurrentOperationCount:。然后我只是将下载请求丢入该队列。但在这种情况下,我不确定如何自己管理连接。 (只需将它们放在一个堆栈上,依靠队列来确保我不会过度/不足?在堆栈中投入dispatch semaphore?)

现在我们正处于Grand Central Dispatch这个勇敢的新世界,是否会开辟其他任何解决方案?乍一看,它看起来并不像,因为GCD的动态扩展并发性的旗舰能力(并在Apple Changing Producer-Consumer Implementations的建议中提到)并没有真正帮助我。但是,我只是在阅读它的表面。

修改

如果重要:是的,我打算使用异步/非阻塞套接字API与服务器进行实际通信。所以I / O本身不必在自己的线程上。我只关心排队工作的机制,并且(安全地)将它们连接到连接上,因为它们可用。

2 个答案:

答案 0 :(得分:1)

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

除此之外,它听起来像您的应用程序需要做的唯一其他工作是维护要下载的项目队列。当任何一个传输完成时,CFSocket回调可以启动队列上下一个项目的传输。 (如果队列为空,则减少连接数,如果有空的队列添加了某些内容,则开始新的传输。)我不明白为什么需要多个线程。

也许你遗漏了一些重要的东西,但根据你的描述,应用程序是I / O绑定的,而不是CPU绑定的,所以所有并发的东西都只是制作更复杂的代码而对性能的影响最小。 / p>

在主线程上完成所有操作。

答案 1 :(得分:0)

为了后人的缘故,在其他地方进行了一些讨论后,我认为我采用的解决方案基本上是:

  • 拥有待处理下载操作的队列,最初为空。
  • 有一个包含所有打开连接的集合,最初为空。
  • 有一个可变数组(真的是队列)空闲打开的连接,最初是空的。
  • 当用户添加下载请求时:
    • 如果空闲连接数组不为空,请删除一个并将下载内容分配给它。
    • 如果没有空闲连接,但总连接数尚未达到其限制,请打开一个新连接,将其添加到该设备,然后将下载内容分配给它。
    • 否则,请将下载排入以供日后使用。
  • 下载完成时:如果有排队请求,请将其取消 把它交给连接;否则,将连接添加到空闲列表。

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

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