简体   繁体   English

为什么主队列上的GCD dispatch_async会导致后台队列出现死锁?

[英]Why does GCD dispatch_async on main queue causes a deadlock from background queue?

I am creating a serial background queue like this: 我正在创建一个这样的串行后台队列:

@property (nonatomic, strong) dispatch_queue_t assetCreationQueue;

// in init...
_assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL);

Then I enumerate ALAsset objects in the background like this: 然后我在后台枚举ALAsset对象,如下所示:

[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop){
    if (asset){
        dispatch_async(weakSelf.assetCreationQueue, ^{
            ALAssetRepresentation *assetRepresentation = [asset defaultRepresentation];
            NSURL *url = [assetRepresentation url];
            if (url) {
                AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:url options:nil];

                // This NSLog fires! avAsset exists.
                NSLog(@"AVURLAsset %@", avAsset);

                dispatch_async(dispatch_get_main_queue(), ^{
                    // This NSLog NEVER fires.
                    // Also tried dispatch_sync.
                    NSLog(@"add to assets array on main queue");
                    [weakSelf.assets insertObject:avAsset atIndex:0];
                });
            }
        });
    }
}];

The assets array property is defined as: assets数组属性定义为:

@property (nonatomic, strong) NSMutableArray *assets;

When I try dispatch_sync(dispatch_get_main_queue(), ^{ I only get ONE NSLog(@"AVURLAsset %@", avAsset); in the console and it indicates that dispatch_sync is causing a deadlock. 当我尝试dispatch_sync(dispatch_get_main_queue(), ^{我只获得一个NSLog(@"AVURLAsset %@", avAsset);在控制台中它表示dispatch_sync导致死锁。

But how can I find out why? 但是我怎么能找出原因呢? I don't see where. 我看不到哪里。 The assetCreationQueue is a background queue and I have to operate on the array only on the main queue. assetCreationQueue是后台队列,我必须仅在主队列上对阵列进行操作。

EDIT: Here is a much more simplified test which also fails: 编辑:这是一个更简化的测试,也失败了:

[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop){
    if (asset){
        dispatch_async(weakSelf.assetCreationQueue, ^{
            if ([NSThread isMainThread]) {
                NSLog(@"already main thread"); // gets called often!!
            } else {
                dispatch_async(dispatch_get_main_queue(), ^{
                    NSLog(@"dispatch async on main queue"); // never gets called!!
                });
            }
        });
     }
}];

So what I don't understand is: Why am I already on the main thread even though I call dispatch_async(weakSelf.assetCreationQueue . It can only lead to evil conclusion: The queue I create is NOT a background queue: 所以我不明白的是:为什么我已经在主线程上,即使我调用dispatch_async(weakSelf.assetCreationQueue 。它只会导致邪恶的结论:我创建的队列不是后台队列:

_assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL);

Why? 为什么?

(Not really an answer, but I want to provide enough information to move forward.) (不是一个真正的答案,但我想提供足够的信息来推进。)

I've tried a very simple program, and this doesn't reproduce at all. 我尝试过一个非常简单的程序,这根本不会重现。 I get many calls to "dispatch async on main queue" and no calls to "already main thread" (the opposite of what you're expecting). 我接到许多调用“在主队列上调度异步”并且没有调用“已经主线程”(与你期望的相反)。 I'm running this on an iPhone 5. Complete code: 我在iPhone 5上运行它。完整的代码:

#import "AppDelegate.h"
@import AssetsLibrary;

@interface AppDelegate ()
@property (nonatomic, strong) dispatch_queue_t assetCreationQueue;
@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  self.assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL);

  ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
  AppDelegate __weak *weakSelf = self;
  [library enumerateGroupsWithTypes:ALAssetsGroupAll
                         usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
                           [group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop){
                             if (asset){
                               dispatch_async(weakSelf.assetCreationQueue, ^{
                                 if ([NSThread isMainThread]) {
                                   NSLog(@"already main thread"); // gets called often!!
                                 } else {
                                   dispatch_async(dispatch_get_main_queue(), ^{
                                     NSLog(@"dispatch async on main queue"); // never gets called!!
                                   });
                                 }
                               });
                             }
                           }];
                         }
                       failureBlock:nil];
  return YES;
}
@end

Note that the use of weakSelf here is unnecessary and dangerous (but if this were the problem, you'd be crashing). 请注意,在这里使用weakSelf是不必要和危险的(但如果这是问题,你就会崩溃)。 You only need weakSelf if the block is going to be stored in a property of an object that self retains. 如果weakSelf块存储在self保留的对象的属性中,则只需要weakSelf It is not necessary or even desired for short-lived blocks like this. 对于像这样的短寿命块,没有必要或甚至不需要。 The way you've used it, if the owning object deallocated in the middle of the enumeration (which it can), weakSelf would become nil, weakSelf.assetCreationQueue would be nil , and dispatch_async() would crash. 你使用它的方式,如果拥有对象在枚举的中间释放(它可以), weakSelf将变为nil, weakSelf.assetCreationQueue将为nildispatch_async()将崩溃。 (But again, this is unlikely the cause of your issue, since it should crash.) (但同样,这不太可能是你的问题的原因,因为它应该崩溃。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 dispatch_async(dispatch_get_main_queue()不适用于iPad,但适用于iPhone - dispatch_async(dispatch_get_main_queue() is not working on iPad, but working on iPhone 对于带有dispatch_async / GCD崩溃的循环 - For loops with dispatch_async / GCD crash iOS GCD:DISPATCH_QUEUE_PRIORITY_BACKGROUND,文件中“磁盘I / O被限制”是什么意思? - iOS GCD: DISPATCH_QUEUE_PRIORITY_BACKGROUND, what does “disk I/O is throttled” mean in document? 除非我使用调度主队列,为什么PopViewControllerAnimated会崩溃? - Why does PopViewControllerAnimated crash unless I use dispatch main queue? 从GCD后台队列添加到UIManagedDocument? - Adding to UIManagedDocument from GCD background queue? 主队列上的dispatch_sync与executeFetchRequest:error之间的死锁: - Deadlock between a dispatch_sync on the main queue and executeFetchRequest:error: 为什么在主队列上调用dispatch_sync()会阻塞主队列? - Why dispatch_sync( ) call on main queue is blocking the main queue? 你可以在GCD / dispatch_async中使用cancel / isCancelled吗? - Can you use cancel/isCancelled with GCD/dispatch_async? 为什么MCNearbyServiceAdvertiser在内部使用调度队列? - Why does MCNearbyServiceAdvertiser use a dispatch queue internally? 从后台启动应用程序时,全局调度队列会阻止主线程UI(启用ARC) - global dispatch queue blocks main thread UIs when app is launched from background (ARC enabled)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM