iOS GCD基本使用及详解

GCD的基本函数:

  • dispatch_sync() 同步执行
  • dispatch_async() 异步执行
  • dispatch_after() 延时执行
  • dispatch_once() 一次性执行
  • dispatch_apply() 提交队列
  • dispatch_queue_create() 创建队列
  • dispatch_group_create() 创建队列组
  • dispatch_group_async() 提交任务到队列组
  • dispatch_group_enter() / dispatch_group_leave() 将队列组中的任务未执行完毕的任务数目加减1(两个函数要配合使用)
  • dispatch_group_notify() 监听队列组执行完毕
  • dispatch_group_wait() 设置等待时间(返回 0成功,1失败)

队列:

GCD 用dispatch queue来处理代码块,这些队列管理你提供给 GCD 的任务并执行这些任务。这就保证了第一个被添加到队列里的任务会是队列中第一个开始的任务,而第二个被添加的任务将第二个开始,如此直到队列的终点。

  • 主队列:系统提供的一个特殊队列。和其它串行队列一样,这个队列中的任务一次只能执行一个。它能保证所有的任务都在主线程执行,而主线程是唯一可用于更新 UI 的线程。这个队列就是用来在主线程上进行操作的。
  • 串行队列:串行队列中的任务一次执行一个,每个任务只会在上一个任务完成时才开始执行。
  • 并发队列:在并发队列中的任务会按照被添加的顺序开始执行,任务可能以任意顺序完成,你不能知道什么时开始执行下一个任务,或者有多少任务在同时执行。
//获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//使用dispatch_get_global_queue()获取全局并发队列,第一个参数是队列优先级,第二个参数传0.
dispatch_queue_t otherQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/** 
  * 使用 dispatch_queue_create 初始化 concurrentQueue 为一个并发队列。
  * 第一个参数是队列标识;第二个参数指定你的队列是串行还是并发。设为NULL时默认是DISPATCH_QUEUE_SERIAL,将创建串行队列.
  * 在必要情况下,你可以将其设置为DISPATCH_QUEUE_CONCURRENT来创建自定义并行队列.
 */
dispatch_queue_t myQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);

同步和异步:

//同步函数,在当前线程执行(不开启新的线程)
dispatch_sync(otherQueue, ^{
    NSLog(@"同步:%@",[NSThread currentThread]);
});

//异步函数,开启子线程执行
dispatch_async(otherQueue, ^{
    NSLog(@"异步:%@",[NSThread currentThread]);
});
打印:
[15276:540462] 同步:{number = 1, name = main}
[15276:540519] 异步:{number = 3, name = (null)}

串行队列:

一个任务执行完毕后,再执行下一个任务

  • 异步
// 1.使用 dispatch_queue_creat()创建串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);

//开启新线程,串行执行任务
NSLog(@"异步函数执行串行队列,当前线程:%@",[NSThread currentThread]);
dispatch_async(serialQueue, ^{
    NSLog(@"任务1:%@",[NSThread currentThread]);
});

dispatch_async(serialQueue, ^{
    NSLog(@"任务2:%@",[NSThread currentThread]);
});

dispatch_async(serialQueue, ^{
    NSLog(@"任务3:%@",[NSThread currentThread]);
});
  • 同步
//不开启新线程,串行执行任务
NSLog(@"同步函数执行串行队列,当前线程:%@",[NSThread currentThread]);

dispatch_sync(serialQueue, ^{
    NSLog(@"任务1:%@",[NSThread currentThread]);
});

dispatch_sync(serialQueue, ^{
    NSLog(@"任务2:%@",[NSThread currentThread]);
});

dispatch_sync(serialQueue, ^{
    NSLog(@"任务3:%@",[NSThread currentThread]);
});
并发队列:
  • 多个任务并发执行(自动开启多个线程同时执行任务)
  • 并发功能只有在异步(dispatch_async)函数下才有效!
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//异步函数,并发队列
//开启新线程,并发执行任务
NSLog(@"异步函数执行并发队列,当前线程:%@",[NSThread currentThread]);
dispatch_async(concurrentQueue, ^{
    NSLog(@"任务1:%@",[NSThread currentThread]);
});

dispatch_async(concurrentQueue, ^{
    NSLog(@"任务2:%@",[NSThread currentThread]);
});

dispatch_async(concurrentQueue, ^{
   NSLog(@"任务3:%@",[NSThread currentThread]);
});
队列组:

任务1,任务2同时执行,所有任务都执行成功后回到主线程,高效率

NSLog(@"队列组执行任务,当前线程:%@",[NSThread currentThread]);
//1.创建队列组 dispatch_group_create()
dispatch_group_t group = dispatch_group_create();

//2.开启任务
//开启任务1
//提交任务到队列组 dispatch_group_async()
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    for (int i = 0; i < 5; i++) {
        NSLog(@"任务1 :%@",[NSThread currentThread]);
    }
});

//开启任务2
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    for (int i = 0; i < 5; i++) {
        NSLog(@"任务2 :%@",[NSThread currentThread]);
    }
});

//所有任务执行完毕,回到主线程进行操作
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"任务1,2执行完毕,回到主线程:%@",[NSThread currentThread]);
});

延时执行:

延迟一段时间把一项任务提交到队列中执行,返回之后就不能取消,常用来在主队列上延迟执行一项任务。

NSLog(@"当前线程 %@", [NSThread currentThread]);
//GCD延时调用(主线程)(主队列)
/**
 * 1.声明一个变量 afterTime 指定要延迟的时长
 * 2.等待 afterTime 给定的时长,再异步地添加一个 Block 到主线程。
 */
dispatch_time_t afterTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC));//1

dispatch_after(afterTime, dispatch_get_main_queue(), ^{//2
    NSLog(@"GCD延时调用(主线程):%@",[NSThread currentThread]);
});

//GCD延时调用(其他线程)(全局并发队列)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"GCD延时调用(其他线程):%@",[NSThread currentThread]);
});
打印:
[15276:540462] 当前线程 {number = 1, name = main}
[15276:540462] GCD延时调用(主线程):{number = 1, name = main}
[15276:540521] GCD延时调用(其他线程):{number = 4, name = (null)}

dispatch_once()

在整个程序运行中,代码会以线程安全的方式执行并且只执行一次

for (int i = 0 ; i < 99999; i++) {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"执行一次:%d",i);
    });
}

dispatch_barrier_async()

读者写者锁(栅栏函数)

  • 在进程管理中起到一个栅栏的作用,它等待所有位于barrier函数之前的操作执行完毕后执行
  • 在barrier函数执行之后,barrier函数之后的操作才会得到执行
    作用:
    1.实现高效率的数据库访问和文件访问
    2.避免数据竞争

使用:

    dispatch_queue_t queue = dispatch_queue_create("barrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    NSLog(@"任务1:%@",[NSThread currentThread]);
});

dispatch_async(queue, ^{
    NSLog(@"任务2:%@",[NSThread currentThread]);
});

dispatch_barrier_async(queue, ^{
    NSLog(@"---barrier---:%@",[NSThread currentThread]);
});

dispatch_async(queue, ^{
    NSLog(@"任务3:%@",[NSThread currentThread]);
});

dispatch_async(queue, ^{
    NSLog(@"任务4:%@",[NSThread currentThread]);
});

解决多个网络请求全部发送后再进行操作的问题方案:

1.Dispatch Group

对多个异步任务的完成进行监控
代码中的注释:

  1. 创建一个新的 Dispatch Group,它相当于一个用来记录未完成任务的计数器。
  • dispatch_group_enter,手动通知 Dispatch Group 任务已经开始
  • dispatch_group_leave,手动通知 Dispatch Group 任务已经完成
  • dispatch_group_notify,当 Dispatch Group 中没有任何任务时会执行。

注意:

  • 必须保证 dispatch_group_enter 和 dispatch_group_leave 成对出现,确保进入 Group 的次数和离开 Group 的次数相等。
    - (IBAction)requestForData:(id)sender
    {
        __block NSMutableDictionary *errDict = [NSMutableDictionary dictionaryWithCapacity:0];
        __block NSMutableDictionary *successDic = [NSMutableDictionary dictionaryWithCapacity:0];

        dispatch_group_t requestGroup = dispatch_group_create();//1
    
        for(NSInteger i=1 ; i<4 ;i++)
        {
            dispatch_group_enter(requestGroup);//2
            [self requestForDataWithIndex:i block:^(NSArray *dataArray, NSInteger index, BOOL isSuccess) {
                if (isSuccess) {
                    NSLog(@"第%ld个网络请求成功,返回参数是:%@",(long)index,dataArray);
                    [successDic setObject:dataArray forKey:[NSNumber numberWithInteger:index]];
                }else{
                    NSLog(@"第%ld个网络请求失败,返回参数是:%@",(long)index,dataArray);
                    [errDict setObject:dataArray forKey:[NSNumber numberWithInteger:index]];
                }
                dispatch_group_leave(requestGroup);//3
            }];
        }
        dispatch_group_notify(requestGroup, dispatch_get_main_queue(), ^{//4
            //请求完成,主线程操作
            NSLog(@"请求全部完成,成功数据:%@,失败数据:%@",successDic,errDict);
        });
    }
打印
2017-03-21 14:11:42.098 GCD-Demo[15276:540462] 发起第1个网络请求:{number = 1, name = main}
2017-03-21 14:11:42.098 GCD-Demo[15276:540462] 发起第2个网络请求:{number = 1, name = main}
2017-03-21 14:11:42.099 GCD-Demo[15276:540462] 发起第3个网络请求:{number = 1, name = main}
2017-03-21 14:11:43.190 GCD-Demo[15276:540462] 第1个网络请求成功,返回参数是:(
1
)
2017-03-21 14:11:43.599 GCD-Demo[15276:540462] 第3个网络请求成功,返回参数是:(
1
)
2017-03-21 14:11:44.599 GCD-Demo[15276:540462] 第2个网络请求失败,返回参数是:(
0
)
2017-03-21 14:11:44.599 GCD-Demo[15276:540462] 请求全部完成,成功数据:{
    3 =     (
        1
    );
    1 =     (
        1
    );
},失败数据:{
    2 =     (
        0
    );
}

2.dispatch_apply()

提交队列,适用于并发循环.

- (IBAction)requestForData_Dispatch_apply:(id)sender
{
/**
 dispatch_apply() 

 @param iterations 迭代的次数
 @param queue 指定任务运行的队列
 @param size_t Block
 */
//    dispatch_apply(size_t iterations, dispatch_queue_t  _Nonnull queue, ^(size_t) {})

    __block NSMutableDictionary *errDict = [NSMutableDictionary dictionaryWithCapacity:0];
    __block NSMutableDictionary *successDic = [NSMutableDictionary dictionaryWithCapacity:0];
    dispatch_group_t requestGroup = dispatch_group_create();
    dispatch_apply(3, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) {
        dispatch_group_enter(requestGroup);
        [self requestForDataWithIndex:i block:^(NSArray *dataArray, NSInteger index, BOOL isSuccess) {
            if (isSuccess) {
                NSLog(@"第%ld个网络请求成功,返回参数是:%@",(long)index,dataArray);
                [successDic setObject:dataArray forKey:[NSNumber numberWithInteger:index]];
            }
            else
            {
                NSLog(@"第%ld个网络请求失败,返回参数是:%@",(long)index,dataArray);
                [errDict setObject:dataArray forKey:[NSNumber numberWithInteger:index]];
            }
            dispatch_group_leave(requestGroup);
        }];
    });
    dispatch_group_notify(requestGroup, dispatch_get_main_queue(), ^{
        //请求完成,主线程操作
        NSLog(@"请求全部完成,成功数据:%@,失败数据:%@",successDic,errDict);
    })
}
打印
2017-03-21 14:15:36.897 GCD-Demo[15276:540462] 发起第0个网络请求:{number = 1, name = main}
2017-03-21 14:15:36.897 GCD-Demo[15276:540521] 发起第1个网络请求:{number = 4, name = (null)}
2017-03-21 14:15:36.897 GCD-Demo[15276:612863] 发起第2个网络请求:{number = 5, name = (null)}
2017-03-21 14:15:37.993 GCD-Demo[15276:540462] 第1个网络请求成功,返回参数是:(
1
)
2017-03-21 14:15:38.897 GCD-Demo[15276:540462] 第0个网络请求成功,返回参数是:(
1
)
2017-03-21 14:15:39.398 GCD-Demo[15276:540462] 第2个网络请求失败,返回参数是:(
0
)
2017-03-21 14:15:39.398 GCD-Demo[15276:540462] 请求全部完成,成功数据:{
0 =     (
    1
);
1 =     (
    1
);
},失败数据:{
2 =     (
    0
);
}

3.Dispatch Semaphore 信号量

  1. 信号量为0时 会阻塞线程,一直等待
  • dispatch_semaphore_wait(信号量,等待时间) 这个函数会使传入的信号量的值-1;
  • dispatch_semaphore_signal (信号量) 这个函数会使传入的信号量的值+1;
  • 正常的使用顺序是先降低然后再提高,这两个函数通常成对使用。

信号量的理解:

  • 信号量相当于一个停车场,创建时的参数相当于提供多少个车位,如果你有两个车位,有4辆车要停,那么,只能让先进来的两个车子停下,后面的两个车子等待,开走一个,才能停入下一个.dispatch_semaphore_wait函数就相当于来了一辆车,调用一次,车位就-1.dispatch_semaphore_signal函数相当于走了一辆车,调用一次,车位就+1.
- (IBAction)requestForData_Dispatch_semaphore_t:(id)sender
{   
    __block NSMutableDictionary *errDict = [NSMutableDictionary dictionaryWithCapacity:0];
    __block NSMutableDictionary *successDic = [NSMutableDictionary dictionaryWithCapacity:0];

    //创建一个信号量。参数指定信号量的起始值(必须大于0)
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    for (NSInteger i=0; i<4; i++) {
    
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//信号量-1
            [self requestForDataWithIndex:i block:^(NSArray *dataArray, NSInteger index, BOOL isSuccess) {
                if (isSuccess) {
                    NSLog(@"第%ld个网络请求成功,返回参数是:%@",(long)index,dataArray);
                    [successDic setObject:dataArray forKey:[NSNumber numberWithInteger:index]];
                }
                else
                {
                    NSLog(@"第%ld个网络请求失败,返回参数是:%@",(long)index,dataArray);
                    [errDict setObject:dataArray forKey:[NSNumber numberWithInteger:index]];
                }
                dispatch_semaphore_signal(semaphore);//信号量+1
            }];
        
        });
    }
}
打印:

这里我代码中设置信号量的起始值为2 ,就会同时发起0和1两个任务.0任务耗时长,1任务先返回后立即发起了任务2,0任务结束后立即发起了任务3.

2017-03-21 14:18:11.150 GCD-Demo[15276:613982] 发起第0个网络请求:{number = 6, name = (null)}
2017-03-21 14:18:11.150 GCD-Demo[15276:612860] 发起第1个网络请求:{number = 7, name = (null)}
2017-03-21 14:18:12.249 GCD-Demo[15276:540462] 第1个网络请求成功,返回参数是:(
1
)
2017-03-21 14:18:12.249 GCD-Demo[15276:613984] 发起第2个网络请求:{number = 8, name = (null)}
2017-03-21 14:18:13.151 GCD-Demo[15276:540462] 第0个网络请求成功,返回参数是:(
1
)
2017-03-21 14:18:13.151 GCD-Demo[15276:613985] 发起第3个网络请求:{number = 9, name = (null)}
2017-03-21 14:18:14.652 GCD-Demo[15276:540462] 第3个网络请求成功,返回参数是:(
1
)
2017-03-21 14:18:14.749 GCD-Demo[15276:540462] 第2个网络请求失败,返回参数是:(
0
)
  • 注: 这里只是为了理解信号量而提供的一种思路...看看就好,不要抬杠...

最后,附上我用来模拟网络请求的代码:

//模拟网络请求
- (void)requestForDataWithIndex:(NSInteger)index block:(Complete)callback
{
    NSLog(@"发起第%ld个网络请求:%@",(long)index,[NSThread currentThread]);
    NSArray * successArray = [NSArray arrayWithObjects:@1, nil];
    NSArray * failureArray = [NSArray arrayWithObjects:@0, nil];
    if (index == 0) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if (callback) {
                callback(successArray,index,YES);
            }
        });
    }
    else if (index == 1) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if (callback) {
                callback(successArray,index,YES);
            }
        });
    }
    else if (index == 2)
    {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if (callback) {
                callback(failureArray,index,NO);
            }
        });
    }
    else if (index == 3)
    {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if (callback) {
                callback(successArray,index,YES);
            }
        });
    }

}

另附上demo链接

Demo在这里下载...
路漫漫其修远兮...

你可能感兴趣的:(iOS GCD基本使用及详解)