所以在一些帮助下,我更清楚嵌套GCD如何在我的程序中运行。
原帖是:
Making sure I'm explaining nested GCD correctly
但是,您不需要浏览原始帖子,但基本上这里的代码在后台运行数据库执行,并且UI响应:
-(void)viewDidLoad {
dispatch_queue_t concurrencyQueue = dispatch_queue_create("com.epam.halo.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t serialQueue = dispatch_queue_create("com.epam.halo.queue2", DISPATCH_QUEUE_SERIAL);
for ( int i = 0; i < 10; i++) {
dispatch_async(concurrencyQueue, ^() {
NSLog(@"START insertion method%d <--", i);
dispatch_sync(serialQueue, ^() {
//this is to simulate writing to database
NSLog(@"----------START %d---------", i);
[NSThread sleepForTimeInterval:1.0f];
NSLog(@"--------FINISHED %d--------", i);
});
NSLog(@"END insertion method%d <--", i);
});
}
}
然而,当我开始重构它们并将它们放入方法并使一切看起来不错时,UI不再响应:
//某个数据库单例类
//串行队列在类的私有扩展中声明。并在init()
中创建-(void)executeDatabaseStuff:(int)i {
dispatch_sync(serialQueue, ^() {
//this is to simulate writing to database
NSLog(@"----------START--------- %d", i);
[NSThread sleepForTimeInterval:1.0f];
NSLog(@"--------FINISHED-------- %d", i);
});
}
-(void)testInsert:(int)i {
dispatch_async(concurrencyQueue, ^() {
[self executeDatabaseStuff:i];
});
}
// ViewController.m
- (void)viewDidLoad {
//UI is unresponsive :(
for ( int i = 0; i < totalNumberOfPortfolios; i++) {
NSLog(@"START insertion method%d <--", i);
[[DatabaseFunctions sharedDatabaseFunctions] testInsert: i];
NSLog(@"END insertion method%d <--", i);
}
}
使重构版本工作的唯一方法是当我放入dispatch_async(dispatch_get_main_queue():
for ( int i = 0; i < totalNumberOfPortfolios; i++) {
dispatch_async(dispatch_get_main_queue(), ^() {
NSLog(@"START insertion method%d <--", i);
[[DatabaseFunctions sharedDatabaseFunctions] testInsert: i];
NSLog(@"END insertion method%d <--", i);
});
}
所以我的问题是,我认为我使用dispatch_async concurrencyQueue这一事实将确保dispatch_sync serialQueue组合不会触及我的主线程。为什么当我将它包装在一个对象/方法中时,我必须使用dispatch_async(dispatch_get_main_queue()...)?
似乎我的主线程是否在并发队列上执行dispatch_async 在viewDidLoad中,或在方法中,确实很重要。
我认为主线程正在将所有这些testInsert方法推送到其线程堆栈中。然后必须由主线程处理这些方法。因此,即使dispatch_sync没有阻塞主线程,主线程也会运行到viewDidLoad的末尾,并且必须等待所有的testInsert方法处理完成才能进入主队列中的下一个任务?
备注
所以我回家再次测试它:
for ( int i = 0; i < 80; i++) {
NSLog(@"main thread %d <-- ", i);
dispatch_async(concurrencyQueue, ^() {
[NSThread isMainThread] ? NSLog(@"its the main thread") : NSLog(@"not main thread");
NSLog(@"concurrent Q thread %i <--", i);
dispatch_sync(serialQueue, ^() {
//this is to simulate writing to database
NSLog(@"serial Q thread ----------START %d---------", i);
[NSThread sleepForTimeInterval:1.0f];
NSLog(@"serial Q thread --------FINISHED %d--------", i);
});
NSLog(@"concurrent Q thread %i -->", i);
});
NSLog(@"main thread %d --> ", i);
} //for loop
当我从1 - 63运行循环时,UI不会被阻止。我在后台看到我的数据库操作处理。
然后当循环为64时,UI被阻止1个数据库操作,然后返回正常。
当我使用65时,UI会冻结2个数据库操作,然后返回正常...
当我使用像80这样的东西时,它会被阻止64-80 ...所以我等待16秒才能让我的UI响应。
当时,我无法弄清楚为什么64.所以现在我知道它的64个并发线程一次被允许。 ...并且与在对象/方法中包装它无关。 :d
非常感谢贡献者的出色帮助!
答案 0 :(得分:3)
您正在发生的事情是将超过64个块提交到您的并发队列,每个块都被[NSThread sleepForTimeInterval:1.0f]
阻止,迫使新线程成为为每个操作创建。 因此,一旦达到线程限制,它就会备份并开始阻止主线程。
我用100&#34;数据库写&#34;测试了这个。操作(在设备上),并且主线程似乎被阻塞,直到36个操作发生(现在只剩下64个操作,因此主线程现在未被阻止)。
使用单例不应该导致任何问题,因为您同步调用方法,因此不应该存在线程冲突。
最简单的解决方案就是使用单个后台串行队列进行数据库写入&#34;操作。这样,只创建了一个线程来处理操作。
- (void)viewDidLoad {
[super viewDidLoad];
static dispatch_once_t t;
dispatch_once(&t, ^{
serialQueue = dispatch_queue_create("com.epam.halo.queue2", DISPATCH_QUEUE_SERIAL);
});
for (int i = 0; i < 100; i++) {
[self testInsert:i];
}
}
-(void)executeDatabaseStuff:(int)i {
//this is to simulate writing to database
NSLog(@"----------START--------- %d", i);
[NSThread sleepForTimeInterval:1.0f];
NSLog(@"--------FINISHED-------- %d", i);
}
-(void)testInsert:(int)i {
NSLog(@"Start insert.... %d", i);
dispatch_async(serialQueue, ^() {
[self executeDatabaseStuff:i];
});
NSLog(@"End insert... %d", i);
}
我不知道为什么在你的for循环中插入dispatch_async(dispatch_get_main_queue(), ^() {}
为你工作...我只能假设它正在卸载&#34;数据库写入&#34;直到界面加载完毕。