Objective c 调度\u同步调度\u获取\u主队列\u导致信号量\u等待\u陷阱

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

我在一个图书馆工作,在后台做一些工作

为了确保从主队列调用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\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提到的不推荐使用的API
currentUserNotificationSettings

在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中结果的代码放入?