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

时间:2014-10-08 05:21:27

标签: 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,
                                         (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次(在许多其他事情中):

2317 Thread_5990845   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 XXXXXXXXX) + 251  [0x10bc7075b]  AppDelegate.m:180
+               2317 -[NSRunningApplication bundleIdentifier]  (in AppKit) + 96  [0x7fff937d8468]
+                 2317 -[NSLock lock]  (in Foundation) + 145  [0x7fff962428bb]
+                   2317 _pthread_mutex_lock  (in libsystem_pthread.dylib) + 372  [0x7fff8e9cf779]
+                     2317 __psynch_mutexwait  (in libsystem_kernel.dylib) + 10  [0x7fff8efc2746]

如果你看第9行,你可以看到我在说什么。

遵循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中的错误,还是我错过了使用dispatch_async的内容。

我在OSX 10.9.5中运行此代码。

1 个答案:

答案 0 :(得分:1)

NSWorkspace对特定的方法是线程安全的。 -frontmostApplication不是其中之一。转到NSWorkspace Class Reference 并查看每种方法的讨论。

请注意+sharedWorkspace-openFile:-openURL:等明确说明:“在OS X v10.6及更高版本中,您可以从应用中的任何线程调用此方法。 “

-frontmostApplication和其他人缺乏线程安全通知。它们目前是不安全的。

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

您只需要在主线程上调用frontmostApplication。

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];
});

注意:这解决了NSWorkspace问题,但您可能在-doTheDinosaurWithAppName:中遇到其他线程问题。

编辑:实际上,Ken是对的(见下面的评论)。

虽然我上面说的一切都是正确的,但我的示例代码也很快地重现了这个问题。 --[NSRunningApplication bundleIdentifier]正在向启动服务守护程序发出同步IPC调用,如果我在每次运行循环迭代时开始查询bundleID,我就能够轻松地使该连接饱和。

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