【问题标题】:Detect when block is added to Grand Central Dispatch?检测何时将块添加到 Grand Central Dispatch?
【发布时间】:2013-10-17 21:45:32
【问题描述】:

我有一个使用 NSThreads 执行并发任务的 iOS 应用程序。我将尝试将其迁移到使用 Grand Central Dispatch (GCD) 来处理并发。

问题在于应用需要有关自给定时间以来创建了多少线程的信息。以及自给定时间以来产生了多少线程当前正在运行。

目前,这是通过创建一个类别来完成的,该类别在NSThread 中的-main 方法上运行一个方法。在新的 swizzled 方法中,它只是增加正在运行的线程总数,然后在新的 swizzled -main 方法返回之前减少相同的变量。

问题是当我使用 GCD dispatch_async 时,它不会创建 NSThread,因此我的类别方法不起作用。如何在使用 GCD 处理并发时达到同样的效果?

我想检测的是何时将新块添加到 GCD,以及何时执行该块。

非常欢迎就如何实现这一目标提出任何建议。

编辑

非常感谢@ipmcc 和@RyanR 帮助我解决这个问题。 :) 我相信我需要更多地介绍一下背景以及我想要完成的工作。

我真正尝试的是扩展 iOS 测试框架FrankFrank 在给定的应用程序中嵌入了一个小型 Web 服务器,它可以向 iOS 应用程序发送 HTTP 请求,从而模拟事件、滑动或点击手势作为示例。

我想以某种方式对其进行扩展,使其能够等到由特定模拟事件触发的所有工作都结束后再根据请求返回。

但是,我发现很难准确检测收到的事件触发了哪些工作。这就是我如何找到解决方案来重置线程计数器,然后在模拟事件后为所有创建的线程增加这个计数器,并在线程完成时减少它。然后阻塞直到线程数再次变为零。我知道这种方法也不完美,它不适用于 GCP。

还有其他方法可以实现吗?我想到的另一种可能的解决方案是指定除了处理 HTTP 请求的线程之外的所有内容都必须同步运行。但是我不知道这是否可能。

关于如何在每个模拟事件之后直到该事件触发的工作完成之前实现阻塞的任何建议?

【问题讨论】:

  • 你能展示一些伪代码来概述你想要做什么吗?

标签: objective-c objective-c-blocks grand-central-dispatch nsthread objective-c-category


【解决方案1】:

问题是应用需要关于有多少 自给定时间以来已创建线程。还有多少线程 是在给定时间当前运行后生成的。

您将无法从 GCD 获取此信息。 GCD 的要点之一是您不管理线程池。它是不透明的。您会注意到,即使是构建 NSThread 和 GCD 的底层线程库 pthreads 也没有(公共)方法来枚举所有现有线程或获取正在运行的线程数。如果没有硬核低级黑客,这将是不可行的。如果您需要控制或知道线程的数量,那么您需要成为产生和管理它们的人,而 GCD 对您来说是错误的抽象。

目前这是通过创建一个执行方法的类别来完成的 在 NSThread 中使用 -main 方法。在新的混合方法中 它只是增加运行的线程总数,然后 在新的 swizzled -main 方法之前减少相同的变量 返回。

请注意,这仅告诉您使用 NSThread 启动的线程数。如前所述,NSThread 是 pthread 之上的一个相当高级的抽象。没有什么可以阻止库代码使用 pthreads API 生成自己的线程,这些线程对您的计数是不可见的。

问题是当我使用 GCD dispatch_async 它不会创建一个 NSThread,因此我的类别方法不起作用。我怎样才能实现 使用 GCD 处理并发时也一样?

简而言之,你不能。如果你想在各种框架上修补功能,那么你应该查找一个名为 mach_override 的库。 (但请不要。)

我想检测的是何时将新块添加到 GCD,并且 该块何时被执行。

由于 GCD 使用线程池,添加一个块的行为并不意味着一个新线程。 (这就是重点。)

如果您有一些有限的资源需要管理其消耗,那么执行此操作的传统方法是使用限制信号量,但这只是一种选择。

整个问题散发着糟糕的设计。与 pthread 的数量一样,GCD 的队列宽度是不透明的/非公开的。您以前的解决方案不是特别可行(如所讨论的那样),进一步的努力可能会产生同样糟糕的解决方案。你真的应该重新考虑你的架构,这样知道有多少线程正在运行并不重要。

编辑:感谢您的澄清。从外部来看,并没有一种通用的方式来判断所有“工作”何时完成。如果一个操作设置了一个十分钟内不会回调的计时器怎么办?在极端情况下,请考虑以下情况:主 runloop 在应用程序的整个生命周期中都会继续旋转,只要主 runloop 还在旋转,就可以在其上完成“工作”。

为了检测“完成”,您的应用必须发出完成信号。为了表示完成,应用必须有某种方式(在自身内部)知道它已经完成。换句话说,应用程序不能告诉其他东西(即弗兰克)它不知道的东西。解决此问题的一种方法是将您在应用程序中所做的所有工作封装在 NSOperations 中。 NSOperation/NSOperationQueue 提供了报告“完成”的好方法。在最简单的级别上,您可以将启动工作的代码包装在 NSBlockOperation 中,然后在该操作中添加一个完成块,在完成时发出其他信号,并将其排入 NSOperationQueue 以执行。 (如果您更喜欢使用 GCD 样式,也可以使用 dispatch_groupdispatch_group_notify 来完成此操作。)

如果您对如何将应用程序的工作打包到 NSOperations 中存在具体问题,我建议您提出一个新问题。

【讨论】:

  • 非常感谢您的回答。根据您的回答,我可以看到使用我想到的方法无法实现我想要的。我已经更新了我的问题,以更清楚地描述我想要完成的工作。如果你有时间,请看看。非常感谢你! :)
【解决方案2】:

您可以挂钩调度内省函数(introspection.h,方法都以dispatch_introspection 开头),但您必须链接到应该仅用于调试的库。我认为您不能将其包含在发布版本中。您最好的选择是将 GCD 封装到您自己的对象中,因此您的所有代码都提交块以通过该对象执行,并在跟踪您感兴趣的任何内容后将它们提交给 GCD。虽然您将无法跟踪线程消耗,因为 GCD 有意将其抽象化并重用线程。

【讨论】:

  • 你认为这种自省可以告诉我给定队列上的所有块是否都已完成执行,而不仅仅是队列是否为空?顺便说一句,我已经更新了我原来的问题,以更详细地描述我想要完成的工作。
  • Introspection 可以在块入队或出队时通知您,但在队列为空时不会通知您
  • 好的,但是它可以告诉我出队的块何时完成吗?
  • 不,没有什么可以。该块可以将其他块排入其他队列、调度计时器、任意数量的异步任务,这使得无法判断块何时“完成”。您需要重新考虑如何尝试设计它,因为它不会起作用。
  • 我不这么认为。将其作为另一个问题发布,您将从更熟悉 LLVM 链接器的人那里得到答案。
猜你喜欢
  • 2012-05-09
  • 1970-01-01
  • 1970-01-01
  • 2011-07-28
  • 2023-03-25
  • 2011-12-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多