iOS面试-使用GCD处理几个线程之间的依赖关系。

有时候我们在开发过程中,会有这样的需求,a任务开始执行的前提是b任务执行完成,c任务开始执行需要等a、b两个异步任务完成,即a依赖于b,c又依赖a,这种需求我们可以使用的GCD来处理。

    //创建分组

    dispatch_group_t group =dispatch_group_create();

    //创建队列

    dispatch_queue_t queue =dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT);

    //往分组中添加任务

    dispatch_group_async(group, queue, ^{

        [NSThreadsleepForTimeInterval:2];//模拟耗时操作

        NSLog(@"11111 %@", [NSThreadcurrentThread]);

    });

    //往分组中添加任务

    dispatch_group_async(group, queue, ^{

        [NSThreadsleepForTimeInterval:1];//模拟耗时操作

        NSLog(@"2222 %@", [NSThreadcurrentThread]);

    });

    //分组中任务完成以后通知该block执行

    dispatch_group_notify(group, queue, ^{

        NSLog(@"完成 %@", [NSThreadcurrentThread]);

        dispatch_async(dispatch_get_main_queue(), ^{

            NSLog(@"通知主线程刷新UI %@", [NSThreadcurrentThread]);

        });

    });

执行结果:

2017-10-24 11:33:40.426 iOSTest[34497:828682] 2222 0x600000260080>{number = 3, name = (null)} 
2017-10-24 11:33:41.426 iOSTest[34497:828660] 11111 0x608000261d80>{number = 4, name = (null)} 
2017-10-2411:33:41.427 iOSTest[34497:828660] 完成 0x608000261d80>{number = 4, name = (null)} 
2017-10-2411:33:41.427 iOSTest[34497:828365] 通知主线程刷新UI 0x60800007b980>{number = 1, name = main}
这样我们使用group可以实现几个任务之间的依赖关系。

然而有时我们的任务一层一层的嵌套了多个Block,这个时候,就应该使用如下代码方式:

//创建分组

    dispatch_group_t group =dispatch_group_create();

    //创建队列

    dispatch_queue_t queue =dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT);

    //往分组中添加任务

    dispatch_group_async(group, queue, ^{

        void (^task)(void) = ^{

            [NSThreadsleepForTimeInterval:2];//模拟耗时操作

            NSLog(@"11111 %@", [NSThreadcurrentThread]);

        };

        dispatch_async(dispatch_get_global_queue(0,0), task);

        NSLog(@"11111---- %@", [NSThreadcurrentThread]);

    });

    //往分组中添加任务

    dispatch_group_async(group, queue, ^{

        void (^task)(void) = ^ {

            [NSThreadsleepForTimeInterval:1];//模拟耗时操作

            NSLog(@"2222 %@", [NSThreadcurrentThread]);

        };

        dispatch_async(dispatch_get_global_queue(0,0), task);

        NSLog(@"2222------- %@", [NSThreadcurrentThread]);

    });

    //分组中任务完成以后通知该block执行

    dispatch_group_notify(group, queue, ^{

        NSLog(@"完成 %@", [NSThreadcurrentThread]);

        dispatch_async(dispatch_get_main_queue(), ^{

            NSLog(@"通知主线程刷新UI %@", [NSThreadcurrentThread]);

        });

    });

执行结果:

2017-10-24 11:44:06.447 iOSTest[34981:881063] 2222------- {number = 4, name = (null)}
2017-10-2411:44:06.447 iOSTest[34981:881046] 11111---- {number = 3, name = (null)}
2017-10-24 11:44:06.448iOSTest[34981:881046] 完成 0x600000071f40>{number = 3, name = (null)} 
2017-10-24 11:44:06.450iOSTest[34981:880987] 通知主线程刷新UI 0x60000006c340>{number = 1, name = main}
 2017-10-2411:44:07.450 iOSTest[34981:881064] 2222 0x6000000708c0>{number = 5, name = (null)} 
2017-10-2411:44:08.452 iOSTest[34981:881049] 11111 0x61000006d5c0>{number = 6, name = (null)}

根据执行结果可以看出,当主线程执行的时候,然而其他两个任务中并没有真正的完成,因为另外两个任务中嵌套了子任务,那问题来了,其他两个任务还没有完成就执行主线程,但是我们需要的是其他两个任务完成才需要执行主线程,别急,group给我们提供了dispatch_group_enter()与dispatch_group_leave()方法来组合运用,值得注意的是,这两个方法一定需要成对使用,要不然有时间又出现一些莫名其妙的bug问题。
代码如下:

//创建分组

    dispatch_group_t group =dispatch_group_create();

    //创建队列

    dispatch_queue_t queue =dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT);

    //往分组中添加任务

    dispatch_group_enter(group);

    dispatch_async(queue, ^{

        void (^task)(void) = ^{

            [NSThreadsleepForTimeInterval:2];//模拟耗时操作

            NSLog(@"11111 %@", [NSThreadcurrentThread]);

            dispatch_group_leave(group);

        };

        dispatch_async(dispatch_get_global_queue(0,0), task);

        NSLog(@"11111---- %@", [NSThreadcurrentThread]);

    });

    //往分组中添加任务

    dispatch_group_enter(group);

    dispatch_async(queue, ^{

        void (^task)(void) = ^ {

            [NSThreadsleepForTimeInterval:1];//模拟耗时操作

            NSLog(@"2222 %@", [NSThreadcurrentThread]);

            dispatch_group_leave(group);

        };

        dispatch_async(dispatch_get_global_queue(0,0), task);

        NSLog(@"2222------- %@", [NSThreadcurrentThread]);

    });

    

    //分组中任务完成以后通知该block执行

    dispatch_group_notify(group, queue, ^{

        NSLog(@"完成 %@", [NSThreadcurrentThread]);

        dispatch_async(dispatch_get_main_queue(), ^{

            NSLog(@"通知主线程刷新UI %@", [NSThreadcurrentThread]);

        });

    });

执行结果如下:

2017-10-24 11:48:05.641 iOSTest[35128:902591] 11111---- {number = 3, name = (null)}
2017-10-2411:48:05.641 iOSTest[35128:902608] 2222------- {number = 4, name = (null)}
2017-10-24 11:48:06.644iOSTest[35128:902609] 2222 0x60000006cb00>{number = 5, name = (null)} 
2017-10-24 11:48:07.644iOSTest[35128:902593] 11111 0x6080000721c0>{number = 6, name = (null)} 
2017-10-24 11:48:07.644iOSTest[35128:902593] 完成 0x6080000721c0>{number = 6, name = (null)} 
2017-10-24 11:48:07.645iOSTest[35128:902524] 通知主线程刷新UI 0x61000006e280>{number = 1, name = main}

这样我们想要的得到的结果就实现了。

A,B,C三个任务并发执行,但是C要等A,B执行完成之后再执行。

信号量:
// 创建一个信号,value:信号量
dispatch_semaphore_create(<#long value#>)
// 使某个信号的信号量+1
dispatch_semaphore_signal(<#dispatch_semaphore_t dsema#>)
// 某个信号进行等待或等待降低信号量 timeout:等待时间,永远等待为 DISPATCH_TIME_FOREVER
dispatch_semaphore_wait(<#dispatch_semaphore_t dsema#>, <#dispatch_time_t timeout#>)
正常的使用顺序是先降低然后再提高,这两个函数通常成对使用。

两种方法

-(void)dispatch_group_function1
{
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_queue_create("com.dispatch.test", DISPATCH_QUEUE_CONCURRENT), ^{
        NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://www.baidu.com"]];
        NSURLSessionDownloadTask *task = [[NSURLSession sharedSession] downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            // 请求完成,可以通知界面刷新界面等操作
            NSLog(@"第一步网络请求完成");
            // 使信号的信号量+1,这里的信号量本来为0,+1信号量为1(绿灯)
            dispatch_semaphore_signal(semaphore);
        }];
        [task resume];
        // 以下还要进行一些其他的耗时操作
        NSLog(@"耗时操作继续进行");
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    });
    dispatch_group_async(group, dispatch_queue_create("com.dispatch.test", DISPATCH_QUEUE_CONCURRENT), ^{
        NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://www.github.com"]];
        NSURLSessionDownloadTask *task = [[NSURLSession sharedSession] downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            // 请求完成,可以通知界面刷新界面等操作
            NSLog(@"第二步网络请求完成");
            // 使信号的信号量+1,这里的信号量本来为0,+1信号量为1(绿灯)
            dispatch_semaphore_signal(semaphore);
        }];
        [task resume];
        // 以下还要进行一些其他的耗时操作
        NSLog(@"耗时操作继续进行");
        dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"刷新界面等在主线程的操作");
    });
}

2019-02-24 18:19:16.251067+0800 Semaphore[33094:12267734] 耗时操作继续进行
2019-02-24 18:19:16.251071+0800 Semaphore[33094:12267735] 耗时操作继续进行
2019-02-24 18:19:16.549563+0800 Semaphore[33094:12267748] 第一步网络请求完成
2019-02-24 18:19:18.091922+0800 Semaphore[33094:12267737] 第二步网络请求完成
2019-02-24 18:19:18.092222+0800 Semaphore[33094:12267662] 刷新界面等在主线程的操作

设定的信号值为2,先执行两个线程,等执行完一个,才会继续执行下一个,保证同一时间执行的线程数不超过2。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //任务1
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 1");
        sleep(1);
        NSLog(@"complete task 1");
        dispatch_semaphore_signal(semaphore);
    });
    //任务2
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 2");
        sleep(1);
        NSLog(@"complete task 2");
        dispatch_semaphore_signal(semaphore);
    });
    //任务3
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 3");
        sleep(1);
        NSLog(@"complete task 3");
        dispatch_semaphore_signal(semaphore);
    });
2019-02-24 18:31:02.447769+0800 Semaphore[33286:12284312] run task 1
2019-02-24 18:31:02.447767+0800 Semaphore[33286:12284310] run task 2
2019-02-24 18:31:03.450756+0800 Semaphore[33286:12284312] complete task 1
2019-02-24 18:31:03.450756+0800 Semaphore[33286:12284310] complete task 2
2019-02-24 18:31:03.450997+0800 Semaphore[33286:12284311] run task 3
2019-02-24 18:31:04.454259+0800 Semaphore[33286:12284311] complete task 3

你可能感兴趣的:(iOS面试-使用GCD处理几个线程之间的依赖关系。)