Objective c 调度\u同步调度\u获取\u主队列\u导致信号量\u等待\u陷阱
我在一个图书馆工作,在后台做一些工作 为了确保从主队列调用UIKit方法,我在NSThread上有以下类别:Objective c 调度\u同步调度\u获取\u主队列\u导致信号量\u等待\u陷阱,objective-c,grand-central-dispatch,Objective C,Grand Central Dispatch,我在一个图书馆工作,在后台做一些工作 为了确保从主队列调用UIKit方法,我在NSThread上有以下类别: +(void) d360_ensureMainThreadSync:(dispatch_block_t) onMainBlock { if ([NSThread isMainThread]) { onMainBlock(); } else { dispatch_sync(dispatch_get_main_queue(), onMainBl
+(void) d360_ensureMainThreadSync:(dispatch_block_t) onMainBlock
{
if ([NSThread isMainThread]) {
onMainBlock();
} else {
dispatch_sync(dispatch_get_main_queue(), onMainBlock);
}
}
并将其用于以下情况:
__block UIUserNotificationSettings *currentSettings;
dispatch_block_t onMainBlock = ^{
currentSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
};
[NSThread d360_ensureMainThreadSync:onMainBlock];
// do something with currentSettings
我需要执行一个dispatch\u sync
,因为我在执行之后立即使用结果
我知道在主线程上调用dispatch\u sync(dispatch\u get\u main\u queue()
会导致死锁,因此需要检查[NSThread isMainThread]
问题是,应用程序有时会冻结,我看到信号灯\u等待\u陷阱中有一个线程
,而其他线程正在等待它完成\u调度\u同步\u等待
这当然是随机发生的,很难重现
下面是当出现这种情况以及我在xcode中暂停调试器时,lldb thread backtrace all
的输出
您可以看到线程1
处于信号量等待陷阱中,而线程5
和线程8
正在调用中等待它
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x0000000183e1cc00 libsystem_kernel.dylib`semaphore_wait_trap + 8
frame #1: 0x00000001023714fc libdispatch.dylib`_dispatch_sema4_wait + 24
frame #2: 0x0000000102372210 libdispatch.dylib`_dispatch_group_wait_slow + 196
frame #3: 0x00000001023760c0 libdispatch.dylib`dispatch_block_wait + 264
frame #4: 0x000000018680a9a4 BaseBoard`-[NSObject(BaseBoard) bs_performSynchronously:timeout:] + 192
frame #5: 0x000000018d9b2754 UIKit`-[UIApplication _userNotificationTypes] + 128
frame #6: 0x000000018d9b2674 UIKit`-[UIApplication currentUserNotificationSettings] + 28
* frame #7: 0x00000001009f8e30 D360Kit`__70-[D360PushNotificationStatusProvider dictionaryWithKeyValueParameters]_block_invoke((null)=0x00000001b4ce8f88) at D360PushNotificationStatusProvider.m:24
frame #8: 0x000000010236945c libdispatch.dylib`_dispatch_client_callout + 16
frame #9: 0x000000010237bdac libdispatch.dylib`_dispatch_sync_thread_bound_invoke + 124
frame #10: 0x000000010236945c libdispatch.dylib`_dispatch_client_callout + 16
frame #11: 0x000000010236e050 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 1192
frame #12: 0x00000001842cbf20 CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
frame #13: 0x00000001842c9afc CoreFoundation`__CFRunLoopRun + 2012
frame #14: 0x00000001841ea2d8 CoreFoundation`CFRunLoopRunSpecific + 436
frame #15: 0x000000018607bf84 GraphicsServices`GSEventRunModal + 100
frame #16: 0x000000018d797880 UIKit`UIApplicationMain + 208
frame #17: 0x000000010057ac18 D360TestApp`main(argc=1, argv=0x000000016f89b9c8) at main.m:11
frame #18: 0x0000000183d0e56c libdyld.dylib`start + 4
thread #5, queue = 'com.d360.EventsNetworkQueue (QOS: UNSPECIFIED)'
frame #0: 0x0000000183e3dc1c libsystem_kernel.dylib`__ulock_wait + 8
frame #1: 0x00000001023716e8 libdispatch.dylib`_dispatch_ulock_wait + 48
frame #2: 0x0000000102371840 libdispatch.dylib`_dispatch_thread_event_wait_slow + 36
frame #3: 0x000000010237baec libdispatch.dylib`_dispatch_sync_wait + 448
frame #4: 0x0000000100a4f9c8 D360Kit`+[NSThread(self=NSThread, _cmd="d360_ensureMainThreadSync:", onMainBlock=0x00000001009f8d6c) d360_ensureMainThreadSync:] at NSThread+D360Thread.m:17
frame #5: 0x00000001009f8aa8 D360Kit`-[D360PushNotificationStatusProvider dictionaryWithKeyValueParameters](self=0x00000001c4013d30, _cmd="dictionaryWithKeyValueParameters") at D360PushNotificationStatusProvider.m:28
frame #6: 0x00000001009f83c8 D360Kit`__75-[NSSet((null)=<unavailable>, provider=0x00000001c4013d30, stop=NO) d360_JSONDictionaryFromParameterProviders]_block_invoke at NSSet+D360ParametersProviders.m:20
frame #7: 0x0000000184206370 CoreFoundation`-[__NSSetM enumerateObjectsWithOptions:usingBlock:] + 224
frame #8: 0x00000001009f8338 D360Kit`-[NSSet(self=8 elements, _cmd="d360_JSONDictionaryFromParameterProviders") d360_JSONDictionaryFromParameterProviders] at NSSet+D360ParametersProviders.m:18
frame #9: 0x00000001009fec24 D360Kit`-[D360DeviceNetworkModel JSONDictionary](self=0x00000001c462c6e0, _cmd="JSONDictionary") at D360DeviceNetworkModel.m:50
frame #10: 0x00000001009ffe90 D360Kit`-[D360EventsNetworkModel JSONDictionary](self=0x00000001c46276e0, _cmd="JSONDictionary") at D360EventsNetworkModel.m:46
frame #11: 0x00000001009d86c8 D360Kit`-[D360Client logEvents:completion:](self=0x00000001c064a2f0, _cmd="logEvents:completion:", eventsModel=0x00000001c46276e0, completion=0x00000001009f305c) at D360Client.m:85
frame #12: 0x00000001009f2c6c D360Kit`-[D360SendEventsOperation start](self=0x00000001c42b68c0, _cmd="start") at D360SendEventsOperation.m:99
frame #13: 0x0000000184cf0004 Foundation`__NSOQSchedule_f + 404
frame #14: 0x000000010236945c libdispatch.dylib`_dispatch_client_callout + 16
frame #15: 0x0000000102376800 libdispatch.dylib`_dispatch_continuation_pop + 592
frame #16: 0x000000010237509c libdispatch.dylib`_dispatch_async_redirect_invoke + 628
frame #17: 0x000000010237ab54 libdispatch.dylib`_dispatch_root_queue_drain + 616
frame #18: 0x000000010237a880 libdispatch.dylib`_dispatch_worker_thread3 + 136
frame #19: 0x0000000183f4f130 libsystem_pthread.dylib`_pthread_wqthread + 1268
frame #20: 0x0000000183f4ec30 libsystem_pthread.dylib`start_wqthread + 4
thread #7, name = 'com.apple.uikit.eventfetch-thread'
frame #0: 0x0000000183e1cbc4 libsystem_kernel.dylib`mach_msg_trap + 8
frame #1: 0x0000000183e1ca3c libsystem_kernel.dylib`mach_msg + 72
frame #2: 0x00000001842cbce4 CoreFoundation`__CFRunLoopServiceMachPort + 196
frame #3: 0x00000001842c98b0 CoreFoundation`__CFRunLoopRun + 1424
frame #4: 0x00000001841ea2d8 CoreFoundation`CFRunLoopRunSpecific + 436
frame #5: 0x0000000184c126e4 Foundation`-[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 304
frame #6: 0x0000000184c31afc Foundation`-[NSRunLoop(NSRunLoop) runUntilDate:] + 96
frame #7: 0x000000018e2e302c UIKit`-[UIEventFetcher threadMain] + 136
frame #8: 0x0000000184d13860 Foundation`__NSThread__start__ + 996
frame #9: 0x0000000183f5032c libsystem_pthread.dylib`_pthread_body + 308
frame #10: 0x0000000183f501f8 libsystem_pthread.dylib`_pthread_start + 312
frame #11: 0x0000000183f4ec38 libsystem_pthread.dylib`thread_start + 4
thread #8, queue = 'com.apple.usernotifications.UNUserNotificationServiceConnection.call-out'
frame #0: 0x0000000183e3dc1c libsystem_kernel.dylib`__ulock_wait + 8
frame #1: 0x00000001023716e8 libdispatch.dylib`_dispatch_ulock_wait + 48
frame #2: 0x0000000102371840 libdispatch.dylib`_dispatch_thread_event_wait_slow + 36
frame #3: 0x000000010237baec libdispatch.dylib`_dispatch_sync_wait + 448
frame #4: 0x0000000100a4f9c8 D360Kit`+[NSThread(self=NSThread, _cmd="d360_ensureMainThreadSync:", onMainBlock=0x0000000100a1a1a0) d360_ensureMainThreadSync:] at NSThread+D360Thread.m:17
frame #5: 0x0000000100a1a108 D360Kit`+[D360ApplicationContext currentContext](self=D360ApplicationContext, _cmd="currentContext") at D360ApplicationContext.m:55
frame #6: 0x0000000100a02a58 D360Kit`-[D360RemoteNotificationController notificationInboxController:didFindActionModel:](self=0x00000001c4259500, _cmd="notificationInboxController:didFindActionModel:", controller=0x00000001c0233960, actionModel=0x00000001c04a1c80) at D360RemoteNotificationController.m:278
frame #7: 0x0000000100a4f378 D360Kit`-[D360NotificationInboxController processNotification:](self=0x00000001c0233960, _cmd="processNotification:", notification=0x00000001c043aee0) at D360NotificationInboxController.m:98
frame #8: 0x0000000100a4ec68 D360Kit`__63-[D360NotificationInboxController processDeliveredNotification]_block_invoke((null)=<unavailable>, notifications=@"16 elements") at D360NotificationInboxController.m:50
frame #9: 0x000000010236949c libdispatch.dylib`_dispatch_call_block_and_release + 24
frame #10: 0x000000010236945c libdispatch.dylib`_dispatch_client_callout + 16
frame #11: 0x0000000102378110 libdispatch.dylib`_dispatch_queue_serial_drain + 692
frame #12: 0x000000010236c9a4 libdispatch.dylib`_dispatch_queue_invoke + 332
frame #13: 0x0000000102379104 libdispatch.dylib`_dispatch_root_queue_drain_deferred_wlh + 424
frame #14: 0x0000000102380100 libdispatch.dylib`_dispatch_workloop_worker_thread + 652
frame #15: 0x0000000183f4efe0 libsystem_pthread.dylib`_pthread_wqthread + 932
frame #16: 0x0000000183f4ec30 libsystem_pthread.dylib`start_wqthread + 4
thread #13, name = 'com.apple.CFSocket.private'
frame #0: 0x0000000183e3d570 libsystem_kernel.dylib`__select + 8
frame #1: 0x00000001842d421c CoreFoundation`__CFSocketManager + 644
frame #2: 0x0000000183f5032c libsystem_pthread.dylib`_pthread_body + 308
frame #3: 0x0000000183f501f8 libsystem_pthread.dylib`_pthread_start + 312
frame #4: 0x0000000183f4ec38 libsystem_pthread.dylib`thread_start + 4
thread #16, name = 'com.apple.NSURLConnectionLoader'
frame #0: 0x0000000183e1cbc4 libsystem_kernel.dylib`mach_msg_trap + 8
frame #1: 0x0000000183e1ca3c libsystem_kernel.dylib`mach_msg + 72
frame #2: 0x00000001842cbce4 CoreFoundation`__CFRunLoopServiceMachPort + 196
frame #3: 0x00000001842c98b0 CoreFoundation`__CFRunLoopRun + 1424
frame #4: 0x00000001841ea2d8 CoreFoundation`CFRunLoopRunSpecific + 436
frame #5: 0x0000000184953b40 CFNetwork`+[NSURLConnection(Loader) _resourceLoadLoop:] + 404
frame #6: 0x0000000184d13860 Foundation`__NSThread__start__ + 996
frame #7: 0x0000000183f5032c libsystem_pthread.dylib`_pthread_body + 308
frame #8: 0x0000000183f501f8 libsystem_pthread.dylib`_pthread_start + 312
frame #9: 0x0000000183f4ec38 libsystem_pthread.dylib`thread_start + 4
thread #21
frame #0: 0x0000000183e3ddbc libsystem_kernel.dylib`__workq_kernreturn + 8
frame #1: 0x0000000183f4efb0 libsystem_pthread.dylib`_pthread_wqthread + 884
frame #2: 0x0000000183f4ec30 libsystem_pthread.dylib`start_wqthread + 4
thread #22
frame #0: 0x0000000183f4ec2c libsystem_pthread.dylib`start_wqthread
现在的问题是什么是同步执行的,以及如何避免它无限期地等待
更新2
如果ensureMainThreadSync
使用dispatch\u async
而不是dispatch\u sync
,我尝试更改我的实现,并使用如下信号量等待它:
+ (void)d360_ensureMainThreadSync:(dispatch_block_t)onMainBlock
{
if ([NSThread isMainThread]) {
onMainBlock();
} else {
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_sync(dispatch_get_main_queue(), ^{
onMainBlock();
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
}
但这没有帮助
我认为这可能确实是因为使用了@CRD提到的不推荐使用的APIcurrentUserNotificationSettings
。在iOS 10+和dispatch\u sync一起运行时,问题代码似乎位于不推荐使用的[[UIApplication sharedApplication]currentUserNotificationSettings]
中(调度获取主队列()
)
工作原理是在iOS 10+中使用UserNotifications
。在我的情况下,我需要以同步方式获取这些值,因此我使用dispatch\u semaphore\t
等待完成块。要与旧iOS版本兼容,代码应该是:
// iOS 10+ use the UNUserNotificationCenter
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion) { .majorVersion = 10, .minorVersion = 0, .patchVersion = 0 }]) {
__block UNNotificationSettings *currentSettings;
// create a semaphore to wait for the asynchronous block
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *_Nonnull settings) {
currentSettings = settings;
dispatch_semaphore_signal(sema);
}];
// the calling code will wait here until getNotificationSettingsWithCompletionHandler completes
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
// handle notification settings
// ...
} else {
__block UIUserNotificationSettings *currentSettings;;
// UKit methods need to be run on a main thread. Implementation of ensureMainThreadSync is above
[NSThread d360_ensureMainThreadSync:^{
currentSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
}];
// handle notification settings
// ...
}
因此,即使我不确定基本问题是什么,这个解决方案对我有效,到目前为止我还没有看到任何死锁。当在iOS 10+中与调度同步(dispatch\u get\u main\u queue()一起运行时,问题代码似乎位于弃用的[[UIApplication sharedApplication]currentUserNotificationSettings]
中
工作原理是在iOS 10+中使用UserNotifications
。在我的情况下,我需要以同步方式获取这些值,因此我使用dispatch\u semaphore\t
等待完成块。要与旧iOS版本兼容,代码应该是:
// iOS 10+ use the UNUserNotificationCenter
if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion) { .majorVersion = 10, .minorVersion = 0, .patchVersion = 0 }]) {
__block UNNotificationSettings *currentSettings;
// create a semaphore to wait for the asynchronous block
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *_Nonnull settings) {
currentSettings = settings;
dispatch_semaphore_signal(sema);
}];
// the calling code will wait here until getNotificationSettingsWithCompletionHandler completes
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
// handle notification settings
// ...
} else {
__block UIUserNotificationSettings *currentSettings;;
// UKit methods need to be run on a main thread. Implementation of ensureMainThreadSync is above
[NSThread d360_ensureMainThreadSync:^{
currentSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
}];
// handle notification settings
// ...
}
所以,即使我不确定基本问题是什么,这个解决方案对我来说是有效的,到目前为止我还没有看到任何死锁。我们能看到bs\u性能同步:超时:
code吗?那不是我的代码。看起来像是苹果的内部框架。我注意到你使用的是不推荐的方法-[UIApplication currentUserNotificationSettings]
。尝试使用不推荐的方法。我知道,但我正在开发一个部署目标为iOS 8.4的库,它的替换UserNotifications
。可能您只需要使用dispatch\u async,而不是使用isMainThread check,并将使用onMainBlock中结果的代码放入。我们可以看到bs\u性能同步:t吗imeout:
code?那不是我的代码。它看起来像是苹果的内部框架。我注意到你正在使用不推荐的方法-[UIApplication currentUserNotificationSettings]
。尝试使用不推荐的方法。我知道,但我正在处理一个部署目标为iOS 8.4的库,其替换UserNotifications
。您可能只需要使用dispatch\u async,而不是使用isMainThread check,并将使用onMainBlock中结果的代码放入?