Iphone 为什么主队列上的GCD dispatch_async会导致后台队列出现死锁?
我正在创建一个串行后台队列,如下所示:Iphone 为什么主队列上的GCD dispatch_async会导致后台队列出现死锁?,iphone,ios,objective-c,concurrency,grand-central-dispatch,Iphone,Ios,Objective C,Concurrency,Grand Central Dispatch,我正在创建一个串行后台队列,如下所示: @property (nonatomic, strong) dispatch_queue_t assetCreationQueue; // in init... _assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL); [group enumerateAssetsUsingBlock:^(ALAsset
@property (nonatomic, strong) dispatch_queue_t assetCreationQueue;
// in init...
_assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL);
[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];
});
}
});
}
}];
然后,我在背景中列举了一组对象,如下所示:
@property (nonatomic, strong) dispatch_queue_t assetCreationQueue;
// in init...
_assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL);
[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];
});
}
});
}
}];
资产数组属性定义为:
@property (nonatomic, strong) NSMutableArray *assets;
当我尝试dispatch_sync(dispatch_get_main_queue(),^{
时,我在控制台中只得到一个NSLog(@“avurlaste%@”,avAsset);
,它表明dispatch_sync
正在导致死锁
但是我怎么才能找到原因呢?我不知道在哪里。assetCreationQueue是一个后台队列,我只能在主队列上对数组进行操作
编辑:
下面是一个更简单的测试,它也会失败:
[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!!
});
}
});
}
}];
所以我不明白的是:为什么我调用dispatch\u async(weakSelf.assetCreationQueue
)却已经在主线程上了。这只会导致错误的结论:我创建的队列不是后台队列:
_assetCreationQueue = dispatch_queue_create("com.mycompany.assetCreationQueue", DISPATCH_QUEUE_SERIAL);
为什么?(这不是一个真正的答案,但我想提供足够的信息,以便继续前进。)
我尝试了一个非常简单的程序,但完全没有重现。我收到许多“在主队列上调度异步”的调用,而没有收到任何“已经主线程”的调用(与您期望的相反)。我正在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
请注意,在这里使用
weakSelf
是不必要的,也是危险的(但如果这是问题所在,您将崩溃)如果块将被存储在一个对象的属性中,你只需要<代码>弱自我>代码>保留。对于这样的短命块,它是不必要的或甚至是不需要的。你使用它的方式,如果拥有的对象在枚举的中间被释放(它可以),weakSelf
将变为零,weakSelf.assetCreationQueue
将变为nil
,并且dispatch\u async()
将崩溃。(但是,这不太可能是问题的原因,因为它应该崩溃。)能否再次检查更新后的代码是否与实际测试的代码相同?缺少括号。@RobNapier修复了此问题。在我尝试在SO代码编辑器中格式化代码时,意外删除了此代码。这就是我正在测试的代码。您的后台队列和主队列似乎是一个整体。您是否可以注销的漏洞elf.assetCreationQueue
返回NSLog(@“%p”,queue);
以确认/反驳这一点?不幸的是,我对这段代码进行了大量的修改,因为它现在已经完全不同了。但AVAssetLibrary-EnumerateAssetSingBlock方法似乎正在使用dispatch_sync(dispatch_get_main_queue(),…)如果我尝试在主队列上调度块,这将导致死锁。谢谢Rob,但没有运气。在我的情况下,AVAssetLibrary枚举器调用的所有块都已在主线程上执行。我进行了更多测试,并从串行队列移到并发队列,并发现在调用dispatch_sync(mainQueue,…)时,它调用dispatch_async(concurrentQueue,…),它调用dispatch_async(mainQueue,…)或dispatch_sync(mainQueue,…),有一个死锁。你是说上面的代码本身在你的设备上不起作用?你运行的是什么版本的iOS?哦,现在我明白了!!!我不知道为什么或者如何,但突然之间你的代码在我的iPhone 5 iOS 7上完美地工作了。起初它似乎也在死锁中被挂起。我认为从我的原始代码来看,罪魁祸首是有一个问题意外成为调度同步的h\u异步调用。因为已经有主线程,调度同步将其锁定。谢谢x1000!接受您的回答。