主线程在viewDidLoad中的并发队列上执行dispatch_async,或者在方法内部执行

时间:2016-01-18 07:14:14

标签: ios multithreading grand-central-dispatch

所以在一些帮助下,我更清楚嵌套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

非常感谢贡献者的出色帮助!

1 个答案:

答案 0 :(得分:3)

There is a hard limit of 64 GCD concurrent operations(每个顶级并发队列)可以一起运行。

您正在发生的事情是将超过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;直到界面加载完毕。

关于线程的更多资源&amp; GCD