dispatch_sync dispatch_get_main_queue导致semaphore_wait_trap

时间:2017-10-10 10:10:03

标签: 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(), onMainBlock);
    }
}

并像这样使用它:

__block UIUserNotificationSettings *currentSettings;

dispatch_block_t onMainBlock = ^{
    currentSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
};

[NSThread d360_ensureMainThreadSync:onMainBlock];

// do something with currentSettings

我需要dispatch_sync执行,因为我之后会立即使用结果。

我知道在主线程上调用dispatch_sync(dispatch_get_main_queue()会导致死锁,因此检查[NSThread isMainThread]

问题是应用程序有时冻结,我发现semaphore_wait_trap中有一个线程,而其他线程正在等待它完成_dispatch_sync_wait

这当然是随机发生的,很难再现。

当情况发生时以及在xcode中暂停调试器时,以下是lldb thread backtrace all的输出。

您可以看到thread #1位于semaphore_wait_trap,而thread #5thread #8正在等待dispatch_sync_wait来自d360_ensureMainThreadSync。< / p>

* 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

更新1

@ Cy-4AH向我指出这个似乎是Apple's internal frameworkbs_performSynchronously

我现在设法使用

重现冻结
@interface NSObject()
- (BOOL)bs_performSynchronously:(id /* block */)arg1;
@end
...
[NSThread d360_ensureMainThreadSync:^{
    [self bs_performSynchronously:^{
        NSLog(@"Test");
    }];
}];

现在问题是这个bs_performSynchronously是什么以及如何避免它无限期地等待。

更新2

我尝试在ensureMainThreadSync使用dispatch_async而不是dispatch_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提及使用了已弃用的API currentUserNotificationSettings

1 个答案:

答案 0 :(得分:0)

在iOS 10+和[[UIApplication sharedApplication] currentUserNotificationSettings]中运行时,有问题的代码似乎已弃用dispatch_sync(dispatch_get_main_queue()

在iOS 10+中使用UserNotifications是有效的。在我的例子中,我需要以同步的方式获取这些值,因此我使用dispatch_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
    // ...
}

所以,即使我不确定基本问题是什么,这个解决方案对我有用,到目前为止我没有看到任何死锁。