主线程在 viewDidLoad 中的并发队列上做 dispatch_async,或者在方法内做事

main thread does dispatch_async on a concurrent queue in viewDidLoad, or within a method matters

所以在一些帮助下,我更加清楚嵌套 GCD 在我的程序中是如何工作的。

原文post位于:

然而,你不需要通过原来的post,但基本上这里的代码运行s数据库在后台执行并且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 不再响应:

//一些数据库单例class

//串行队列在class的私有扩展中声明。并在 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 组合触及。为什么当我用 object/method 包装它时,我必须使用 dispatch_async(dispatch_get_main_queue()...) ?

似乎我的主线程是否在并发队列上执行 dispatch_async 在 viewDidLoad 中,或在方法中,确实很重要。

我认为主线程正在将所有这些 testInsert 方法推送到它的线程堆栈中。然后这些方法必须由主线程处理。因此,即使 dispatch_sync 没有阻塞主线程,主线程 运行s 到 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次数据库操作,然后returns没问题。

当我使用 65 时,UI 冻结了 2 个数据库操作,然后 returns 没问题...

当我使用 80 之类的东西时,它会从 64-80 被阻止...所以我等了 16 秒后我的 UI 才响应。

当时我想不通为什么是64。所以现在我知道它同时允许64个并发线程。 ...并且与将其包装在 object/method 中无关。 :D

非常感谢贡献者的大力帮助!

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

发生的事情是您向并发队列提交 超过 64 个块,每个块都被 [NSThread sleepForTimeInterval:1.0f] 阻塞,从而强制创建一个新线程对于每个操作。 因此,一旦达到线程限制,它就会备份并开始阻塞主线程。

我已经用 100 个“数据库写入”操作(在设备上)对此进行了测试,主线程似乎被阻塞,直到发生了 36 个操作(现在只剩下 64 个操作,因此主线程现在是un-blocked).

使用单例不会给您带来任何问题,因为您是同步调用方法的,因此不应该有线程冲突。

最简单的解决方案就是为您的“数据库写入”操作使用单个后台串行队列。这样,只创建一个线程来处理操作。

- (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(), ^() {} 对你有用...我只能假设它是 off-loading 直到界面加载后的“数据库写入” .

有关线程和 GCD 的更多资源