Objective c NSRunningApplication bundleIdentifier锁定我的异步应用程序

Objective c NSRunningApplication bundleIdentifier锁定我的异步应用程序,objective-c,macos,grand-central-dispatch,appkit,Objective C,Macos,Grand Central Dispatch,Appkit,我正在由GCD提供服务的后台线程中检索NSRunningApplication的bundleIdentifier,我使用的代码基本上如下所示: NSWorkspace * __block workspace = [NSWorkspace sharedWorkspace]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,

我正在由GCD提供服务的后台线程中检索
NSRunningApplication
bundleIdentifier
,我使用的代码基本上如下所示:

NSWorkspace * __block workspace = [NSWorkspace sharedWorkspace];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,
                                         (unsigned long)NULL), ^(void) {
    NSString *dateString = nil;
    dateString = [dateFormat stringFromDate:[NSDate date]];

    // here's where my app is getting locked
    [foo doTheDinosaurWithAppName:[[workspace frontmostApplication] bundleIdentifier];
});
我面临的问题是,在随机情况下,此调用会阻塞任务,使其无法完成,这将在多个线程中发生,直到我达到超过64个锁定线程的点,并且我的应用程序因超出限制而终止

当使用Activity Monitor查看流程信息时,我发现以下跟踪重复了大约67次(除其他外):

如果你看第九行,你就会明白我在说什么

按照Ken的建议,有一些痕迹(可能6条)看起来有点不同,但它们似乎都指向同一条线:

2317 Thread_5993821   DispatchQueue_4: com.apple.root.low-priority  (concurrent)
+ 2317 start_wqthread  (in libsystem_pthread.dylib) + 13  [0x7fff8e9d0fb9]
+   2317 _pthread_wqthread  (in libsystem_pthread.dylib) + 314  [0x7fff8e9cdef8]
+     2317 _dispatch_worker_thread2  (in libdispatch.dylib) + 40  [0x7fff90678177]
+       2317 _dispatch_root_queue_drain  (in libdispatch.dylib) + 326  [0x7fff90677082]
+         2317 _dispatch_client_callout  (in libdispatch.dylib) + 8  [0x7fff9067528d]
+           2317 _dispatch_call_block_and_release  (in libdispatch.dylib) + 12  [0x7fff906781bb]
+             2317 __45-[AppDelegate applicationDidFinishLaunching:]_block_invoke_2  (in Keystats) + 251  [0x10bc7075b]  AppDelegate.m:180
+               2317 -[NSRunningApplication bundleIdentifier]  (in AppKit) + 164  [0x7fff937d84ac]
+                 2317 -[NSRunningApplication _fetchStaticInformationWithAtLeastKey:]  (in AppKit) + 94  [0x7fff93565a97]
+                   2317 _LSCopyApplicationInformation  (in LaunchServices) + 2214  [0x7fff8f165adf]
+                     2317 LSClientToServerConnection::LSClientToServerConnection(int, __CFDictionary const*, bool)  (in LaunchServices) + 255  [0x7fff8f161543]
+                       2317 LSClientToServerConnection::setupServerConnection(int, __CFDictionary const*)  (in LaunchServices) + 160  [0x7fff8f1616f4]
+                         2317 xpc_connection_send_message_with_reply_sync  (in libxpc.dylib) + 195  [0x7fff9a75f7ef]
+                           2317 _dispatch_semaphore_wait_slow  (in libdispatch.dylib) + 206  [0x7fff906799f9]
+                             2317 semaphore_wait_trap  (in libsystem_kernel.dylib) + 10  [0x7fff8efbea56]
我不知道这是否是
NSRunningApplication
bundleIdentifier
中的一个bug,或者我使用
dispatch\u async
时是否遗漏了什么


我在OSX 10.9.5中运行这段代码。

NSWorkspace对于特定方法是线程安全的<代码>-frontmostApplication不是其中之一。转到并查看每个方法的讨论

请注意,
+sharedWorkspace
-openFile:
-openURL:
,等等明确声明:“在OS X v10.6及更高版本中,从应用程序中的任何线程调用此方法都是安全的。”

-frontmostApplication
和其他应用程序缺少螺纹安全注意事项。它们目前是线程不安全的

仅仅因为API是线程不安全的,并不意味着当您在后台线程上调用它时,它将100%挂起。它将完全按照您所看到的那样运行:它将在大部分时间工作,偶尔会死锁

您只需在主线程上调用frontmostApplication

注意:这修复了NSWorkspace问题,但在
-doTheDinosaurWithAppName:
中可能存在其他线程问题

编辑:事实上,肯是对的(见下面的评论)

虽然我上面所说的一切都是正确的,但我的示例代码也相当快地再现了这个问题<代码>-[NSRunningApplication bundleIdentifier]正在向启动服务守护程序发出同步IPC调用,如果我开始在每次运行循环迭代中查询bundleId,我很容易使该连接饱和


如果我每秒只查询bundleID 100次,一切都正常。

您是否尝试过从主线程运行它,因为工作区不是线程安全的?其他线程在做什么?您是否让主线程运行事件循环?@Wain NSWorkspace在大多数情况下(在后台线程中)都可以正常工作。这种情况很少发生。@kenthomas所有其他线程都在做同样的事情,它们获取最前面的应用程序包id,然后以相同的方式处理它。我让主线程运行事件循环,我没有在主线程中运行任何东西(除了GUI更改)。它们是否都在完全相同的位置被阻止?想必,有什么东西成功地锁定了其他人试图锁定的那把锁,但随后继续并在其他地方卡住了。你想找到另一个,看看它卡在哪里。(有可能是某个bug导致锁定它的线程无法解锁,即使它没有被卡住。但我认为这是一个较低的概率。)也许。我通常也是一个固执的人,因为我只依赖于有文件证明的线程安全承诺。但是,
-runningApplications
记录为线程安全,而
NSRunningApplication
的所有属性(包括
活动的
)记录为线程安全。因此,以线程安全的方式实现
-frontmostApplication
是相当简单的。此外,所观察到的挂起是在
NSRunningApplication
代码内部,因此
-frontmostApplication
显然起了作用,而
NSRunningApplication
内部的某些东西被破坏了,尽管它应该是线程安全的。实际上,您是正确的。好电话。当我用我的代码做一个示例项目时,它显示了相同的问题。如果您开始在每个运行循环周期要求bundleID,则项目排队的速度将快于ls守护进程(bundleID最终阻止了它)的响应速度。感谢您的精彩讨论!不确定我是否应该将这个问题标记为已回答,因为这似乎可能是一个bug。有什么建议吗?老实说,我觉得这个问题还没有完全解决。您是否可以发布更多关于调用
-bundleIdentifier
的块的频率的代码?如果您在每次运行循环迭代中都调用它,那么您肯定处于队列随时间累积的情况。
2317 Thread_5993821   DispatchQueue_4: com.apple.root.low-priority  (concurrent)
+ 2317 start_wqthread  (in libsystem_pthread.dylib) + 13  [0x7fff8e9d0fb9]
+   2317 _pthread_wqthread  (in libsystem_pthread.dylib) + 314  [0x7fff8e9cdef8]
+     2317 _dispatch_worker_thread2  (in libdispatch.dylib) + 40  [0x7fff90678177]
+       2317 _dispatch_root_queue_drain  (in libdispatch.dylib) + 326  [0x7fff90677082]
+         2317 _dispatch_client_callout  (in libdispatch.dylib) + 8  [0x7fff9067528d]
+           2317 _dispatch_call_block_and_release  (in libdispatch.dylib) + 12  [0x7fff906781bb]
+             2317 __45-[AppDelegate applicationDidFinishLaunching:]_block_invoke_2  (in Keystats) + 251  [0x10bc7075b]  AppDelegate.m:180
+               2317 -[NSRunningApplication bundleIdentifier]  (in AppKit) + 164  [0x7fff937d84ac]
+                 2317 -[NSRunningApplication _fetchStaticInformationWithAtLeastKey:]  (in AppKit) + 94  [0x7fff93565a97]
+                   2317 _LSCopyApplicationInformation  (in LaunchServices) + 2214  [0x7fff8f165adf]
+                     2317 LSClientToServerConnection::LSClientToServerConnection(int, __CFDictionary const*, bool)  (in LaunchServices) + 255  [0x7fff8f161543]
+                       2317 LSClientToServerConnection::setupServerConnection(int, __CFDictionary const*)  (in LaunchServices) + 160  [0x7fff8f1616f4]
+                         2317 xpc_connection_send_message_with_reply_sync  (in libxpc.dylib) + 195  [0x7fff9a75f7ef]
+                           2317 _dispatch_semaphore_wait_slow  (in libdispatch.dylib) + 206  [0x7fff906799f9]
+                             2317 semaphore_wait_trap  (in libsystem_kernel.dylib) + 10  [0x7fff8efbea56]
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    __block NSString *bundleIdentifier;

    dispatch_sync(dispatch_get_main_queue(), ^{
         bundleIdentifier = [[[NSWorkspace sharedWorkspace] frontmostApplication] bundleIdentifier];
    });

    [foo doTheDinosaurWithAppName:bundleIdentifier];
});