面试总结 | GCD

GCD:

1,主队列,主队列创建的任务是主线程任务。

主线程只能用于异步队列,否则会造成死锁,主队列是放在主线程队列当中,如果用同步,会造成主线程多个任务同时等待彼此任务,导致死锁,例如:

    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    //sync,同步会导致死锁
        dispatch_sync(mainQueue, ^{
            NSLog(@"dispatch_get_main_queue - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]);
    });
//所以正确的为:
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    //sync,同步会导致死锁
        dispatch_async(mainQueue, ^{
            NSLog(@"dispatch_get_main_queue - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]);
    });

2,全局队列,也是并发队列,其创建方式为:

    dispatch_queue_t gQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(gQueue, ^{
        NSLog(@"dispatch_get_main_queue - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]);
    });

两个参数,第一个参数是优先级,根据优先级优先访问当前队列,第二个值苹果规定为0,否则会返回空队列。

首先,如果全局队列执行异步操作,系统会自动创建一个子线程去执行当前异步任务,如上代码,输出的为:

dispatch_get_main_queue - - - <NSThread: 0x600002558c40>{number = 5, name = (null)} - - - 0

如果全局队列执行同步操作,系统不会创建新的线程,会在主线程中执行当前操作,如下:

    dispatch_queue_t gQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_sync(gQueue, ^{
        NSLog(@"dispatch_get_main_queue - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]);
    });
//输出为:
dispatch_get_main_queue - - - <NSThread: 0x600003e87e00>{number = 1, name = main} - - - 1
//显示为主线程

3,用户队列,也就是自定义队列,其创建方式为:

    dispatch_queue_t userQueue = dispatch_queue_create("com.ashes.userQueue", NULL);
    dispatch_async(userQueue, ^{
       NSLog(@"dispatch_queue_create - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]);
    });

上述代码中执行了异步操作,所以系统会自动创建一个子线程去执行当前用户队列中的任务,如下输出:

dispatch_queue_create - - - <NSThread: 0x600002790900>{number = 5, name = (null)} - - - 0
//表明当前线程不是主线程,是系统自动创建的子线程

如果是同步操作,如下:

    dispatch_queue_t userQueue = dispatch_queue_create("com.ashes.userQueue", NULL);
    dispatch_sync(userQueue, ^{
       NSLog(@"dispatch_queue_create - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]);
    });
//输出,显示为主线程
dispatch_queue_create - - - <NSThread: 0x600000415380>{number = 1, name = main} - - - 1

用户队列分为并发队列和非并发队列,具体创建的时候根据传递的参数决定,DISPATCH_QUEUE_CONCURRENT,并发队列;
DISPATCH_QUEUE_SERIAL,非并发(串行队列)队列;
并发队列执行异步操作,会创建子线程;并发队列执行同步操作,不会创建子线程;
非并发队列执行异步操作,会创建子线程;非并发队列执行同步操作,不会创建主线程。

4,延时操作。规定一定的时间以后去执行某个任务,其中执行的线程可是主线程,也可是子线程,例如:

//用户队列延时操作,属于子线操作。
dispatch_queue_t userQueue = dispatch_queue_create("com.ashes.userQueue", NULL);
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); // 1
dispatch_after(popTime, userQueue, ^(void){ // 2
    NSLog(@"2, dispatch_queue_create - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]);
});
//输出
2, dispatch_queue_create - - - <NSThread: 0x6000026efc40>{number = 3, name = (null)} - - - 0
/*****主线程队列延时操作*****/
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); // 1
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // 2
    NSLog(@"2, dispatch_queue_create - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]);
});
//输出
2, dispatch_queue_create - - - <NSThread: 0x600003050d40>{number = 1, name = main} - - - 1
/*****全局队列延时操作*****/
//全局队列操作,任务属于子线操作
dispatch_queue_t gQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); // 1
dispatch_after(popTime, gQueue, ^(void){ // 2
    NSLog(@"2, dispatch_queue_create - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]);
});

所以主线程队列是在主进程中执行,全局队列和用户队列,系统会自动创建子线程,然后执行任务。如果当前队列(全局队列和用户队列)中的任务和主线程任务是同步,那么得等其上边的任务执行完之后再去执行其队列的任务,所以定义为主线程操作。

5,文件读取锁操作,当一个文件被多个地方写入的时候,可能会对文件造成损坏或者不完整,那么需要文件需要加锁操作,一般情况都是写入的时候加锁,读取的时候不需要。文件锁需要两个栅栏函数:dispatch_barrier_sync,dispatch_barrier_async。

dispatch_barrier_sync会将队列中的任务进行分割,等待执行dispatch_barrier_sync函数前面的任务执行,然后再执行dispatch_barrier_sync中的任务,其次再执行其下边的任务,且它是在主线程中执行,会阻塞主线程;
dispatch_barrier_async会将队列中的任务进行分割,等待执行dispatch_barrier_sync函数前面的任务执行,然后再执行dispatch_barrier_async中的任务,其次再执行其下边的任务,且它是在子线程中执行,不会阻塞主线程;
例如:

    dispatch_queue_t userQueue = dispatch_queue_create("com.ashes.userQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(userQueue, ^{
        NSLog(@"11111111111%@",[NSThread currentThread].description);
    });
    dispatch_async(userQueue, ^{
        NSLog(@"00000000000%@",[NSThread currentThread].description);
    });
    dispatch_barrier_sync(userQueue, ^{
        NSLog(@"dispatch_barrier_async - - - %@ - - - %d\n",[NSThread currentThread].description,[NSThread isMainThread]);
    });
    dispatch_async(userQueue, ^{
        NSLog(@"2222222222%@",[NSThread currentThread].description);
    });
    dispatch_async(userQueue, ^{
        NSLog(@"3333333333%@",[NSThread currentThread].description);
    });
    dispatch_async(userQueue, ^{
        NSLog(@"4444444444%@",[NSThread currentThread].description);
    });
//输出:
2020-11-12 16:10:26.471394+0800 xxxxDemo[84233:5342588] 00000000000<NSThread: 0x600000c75680>{number = 5, name = (null)}
2020-11-12 16:10:26.471394+0800 xxxxDemo[84233:5342592] 11111111111<NSThread: 0x600000c17b40>{number = 6, name = (null)}
2020-11-12 16:10:26.471627+0800 xxxxDemo[84233:5342469] dispatch_barrier_async - - - <NSThread: 0x600000c38440>{number = 1, name = main} - - - 1
2020-11-12 16:10:26.471852+0800 xxxxDemo[84233:5342592] 2222222222<NSThread: 0x600000c17b40>{number = 6, name = (null)}
2020-11-12 16:10:26.471853+0800 xxxxDemo[84233:5342588] 3333333333<NSThread: 0x600000c75680>{number = 5, name = (null)}
2020-11-12 16:10:26.471859+0800 xxxxDemo[84233:5342584] 4444444444<NSThread: 0x600000c020c0>{number = 3, name = (null)}

dispatch_barrier_async和dispatch_barrier_sync的区别为:dispatch_barrier_async不阻塞主线程;
dispatch_barrier_sync阻塞主线程,非得等到栅栏里的任务执行完成程序才能执行主线程的任务;
栅栏函数只对主队列和自身所在队列有影响,其他队列不受影响。

网上贴出来一个多读单写的列子:

- (id)readDataForKey:(NSString *)key
{
    __block id result;
    dispatch_sync(_concurrentQueue, ^{
        result = [self valueForKey:key];
    });
    return result;
}
- (void)writeData:(id)data forKey:(NSString *)key
{
    dispatch_barrier_async(_concurrentQueue, ^{
        [self setValue:data forKey:key];
    });
}

6,线程组,dispatch_group_t,用于处理较复杂的一些操作。

具体项目中用到的场景,例如多图片上传,需要上传完之后通知主线程做某种操作,如下代码:

dispatch_queue_t userQueue = dispatch_queue_create("userQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
for (NSInteger i = 0; i < 10; i++) {
    dispatch_group_async(group, userQueue, ^{
        //这里可以进行上传图片
        sleep(1);
        NSLog(@"%ld - - - %@ \n",i,[NSThread currentThread].description);
    });
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"刷新页面");
});
/******输出******/
2020-11-07 16:52:56.789853+0800 git_test[45646:2714650] 0 - - - <NSThread: 0x600003e52680>{number = 3, name = (null)}
2020-11-07 16:52:56.789991+0800 git_test[45646:2714657] 6 - - - <NSThread: 0x600003e5c000>{number = 7, name = (null)}
2020-11-07 16:52:56.790058+0800 git_test[45646:2714658] 7 - - - <NSThread: 0x600003e5c040>{number = 8, name = (null)}
2020-11-07 16:52:56.790158+0800 git_test[45646:2714647] 1 - - - <NSThread: 0x600003e5c080>{number = 9, name = (null)}
2020-11-07 16:52:56.790194+0800 git_test[45646:2714649] 3 - - - <NSThread: 0x600003e5c0c0>{number = 10, name = (null)}
2020-11-07 16:52:56.790223+0800 git_test[45646:2714648] 2 - - - <NSThread: 0x600003e52740>{number = 4, name = (null)}
2020-11-07 16:52:56.791025+0800 git_test[45646:2714656] 5 - - - <NSThread: 0x600003e53040>{number = 6, name = (null)}
2020-11-07 16:52:56.790588+0800 git_test[45646:2714655] 4 - - - <NSThread: 0x600003e53080>{number = 5, name = (null)}
2020-11-07 16:52:56.796564+0800 git_test[45646:2714660] 9 - - - <NSThread: 0x600003e5c380>{number = 11, name = (null)}
2020-11-07 16:52:56.796633+0800 git_test[45646:2714659] 8 - - - <NSThread: 0x600003e52900>{number = 12, name = (null)}
2020-11-07 16:52:56.796839+0800 git_test[45646:2714582] 刷新页面
//但是以上上传有一个问题就是,for循环里边dispatch_group_async的异步操作,不是按照顺序进行,如果类型微博里边的9张图,用户必须按照顺序去拼图,那么会有一些误差,所以修改为:
dispatch_queue_t userQueue = dispatch_queue_create("userQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, userQueue, ^{
for (NSInteger i = 0; i < 10; i++) {
     NSLog(@"%ld - - - %@ \n",i,[NSThread currentThread].description);
  }
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"刷新页面");
});
//这样就可以按照顺序去上传了
/******输出******/
2020-11-07 16:54:32.754027+0800 git_test[45673:2716334] 0 - - - <NSThread: 0x600003d58d40>{number = 5, name = (null)}
2020-11-07 16:54:32.754199+0800 git_test[45673:2716334] 1 - - - <NSThread: 0x600003d58d40>{number = 5, name = (null)}
2020-11-07 16:54:32.754322+0800 git_test[45673:2716334] 2 - - - <NSThread: 0x600003d58d40>{number = 5, name = (null)}
2020-11-07 16:54:32.754437+0800 git_test[45673:2716334] 3 - - - <NSThread: 0x600003d58d40>{number = 5, name = (null)}
2020-11-07 16:54:32.754543+0800 git_test[45673:2716334] 4 - - - <NSThread: 0x600003d58d40>{number = 5, name = (null)}
2020-11-07 16:54:32.754683+0800 git_test[45673:2716334] 5 - - - <NSThread: 0x600003d58d40>{number = 5, name = (null)}
......
2020-11-07 16:54:32.770803+0800 git_test[45673:2716193] 刷新页面

dispatch_group_async于dispatch_group_notify配合使用,dispatch_group_async只有异步方法,没有dispatch_group_sync。 当dispatch_group_async中的执行完会通知dispatch_group_notify去执行任务。

7,希望在应用的生命周期内仅被调度一次的代码块函数,dispatch_once,通常用于单例创建,目的是为了线程安全操作:

+ (id)sharedsharedTestManager
{
    static dispatch_once_t onceQueue;
    static sharedTestManager *sharedTestManager = nil;
 
    dispatch_once(&onceQueue, ^{ sharedTestManager = [[self alloc] init]; });
    return sharedTestManager;
}
//=====================
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);

8,信号量,dispatch_semaphore_t。用来控制访问资源的数量的标识,其主要作用是:
1,保持线程同步,将异步执行任务转换为同步执行任务;
2,保证线程安全,为线程加锁。

创建信号量,参数:
1),信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量值)
//等待降低信号量
dispatch_semaphore_wait(信号量,等待时间)
//提高信号量dispatch_semaphore_signal(信号量)
创建的参数value是指,在当前信号操作下,同事有几个线程并发操作,不能小于0,如下例子:

以上多图片顺序上传亦可用此操作:

    dispatch_queue_t userQueue = dispatch_queue_create("userQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    for (NSInteger i = 0; i < 10; i++) {
        dispatch_async(userQueue, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            //这里可以进行图片上传操作
            NSLog(@"begin %ld",i);
            sleep(1);
            NSLog(@"end %ld",i);
            dispatch_semaphore_signal(semaphore);
        });
    }
//输出则按照顺序一个一个去执行,如果value为2,则会双双去执行:
2020-11-07 17:25:25.135139+0800 git_test[46129:2742208] begin 0
2020-11-07 17:25:26.135419+0800 git_test[46129:2742208] end 0
2020-11-07 17:25:26.135602+0800 git_test[46129:2742211] begin 2
2020-11-07 17:25:27.140482+0800 git_test[46129:2742211] end 2
2020-11-07 17:25:27.140739+0800 git_test[46129:2742210] begin 1
2020-11-07 17:25:28.141881+0800 git_test[46129:2742210] end 1
2020-11-07 17:25:28.142088+0800 git_test[46129:2742218] begin 3
2020-11-07 17:25:29.145829+0800 git_test[46129:2742218] end 3
2020-11-07 17:25:29.146019+0800 git_test[46129:2742209] begin 4
2020-11-07 17:25:30.147681+0800 git_test[46129:2742209] end 4
2020-11-07 17:25:30.147994+0800 git_test[46129:2742222] begin 5
2020-11-07 17:25:31.152211+0800 git_test[46129:2742222] end 5
2020-11-07 17:25:31.152491+0800 git_test[46129:2742223] begin 6
2020-11-07 17:25:32.156475+0800 git_test[46129:2742223] end 6
2020-11-07 17:25:32.157495+0800 git_test[46129:2742224] begin 7
2020-11-07 17:25:33.159167+0800 git_test[46129:2742224] end 7
2020-11-07 17:25:33.159422+0800 git_test[46129:2742225] begin 8
2020-11-07 17:25:34.162234+0800 git_test[46129:2742225] end 8
2020-11-07 17:25:34.162526+0800 git_test[46129:2742226] begin 9
2020-11-07 17:25:35.166981+0800 git_test[46129:2742226] end 9




Leave a Reply

Required fields are marked *