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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352