GCD进阶版

GCD相关可挖掘的点有很多,记录一些面试题,持续更新。

第一题: dispatch_sync和 dispatch_async相关

下面的代码会打印什么?

- (void)testSync
{
    NSLog(@"111");
    dispatch_queue_t queue = dispatch_queue_create("com.testGCD.dispatchQueue", NULL);
    dispatch_sync(queue, ^{
        NSLog(@"block");
    });
    NSLog(@"222");
}
- (void)testAsync
{
    //主线程,异步
    NSLog(@"111");
    dispatch_queue_t queue = dispatch_queue_create("com.testGCD.dispatchQueue", NULL);
    dispatch_async(queue, ^{
        sleep(2.0);
        NSLog(@"block");
    });
    NSLog(@"222");
}

解答:第一个会造成死锁,主线程内(串行)执行同步任务
实际运行效果如下

第二个打印如下:

主线程增加异步任务,不阻塞。所以先打印111,再打印222. 等两秒后打印block

第二题:GCD中如何设置队列中执行任务的最大并发数

先看一下 NSOperationQueue

  • NSOperationQueue 提供maxConcurrentOperationCount属性可直接设置
  • 默认值为NSOperationQueueDefaultMaxConcurrentOperationCount = -1 ,表示并不做限制
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 5;

解答:GCD 中并未暴露直接属性,可以通过dispatch_semaphore_t 实现,具体实现如下:

 //创建执行任务的并发队列,也可用dispatch_group_t代替
    dispatch_queue_t workConcurrentQueue = dispatch_queue_create("cccccccc", DISPATCH_QUEUE_CONCURRENT);
    //创建存放任务的串行队列
    dispatch_queue_t serialQueue = dispatch_queue_create("sssssssss",DISPATCH_QUEUE_SERIAL);
    //创建 最大并发数为2
    int count = 2;
    //创建对应并发数的信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(count);

    //往执行任务的串行队列中,加入5个异步任务
    for (NSInteger i = 0; i < 5; i++) {
        dispatch_async(serialQueue, ^{
            //当信号总量等于0的时候就会一直等待(此时信号总量不-1),否则就可以正常的执行,并且信号总量-1。
            //当添加到第count个任务时,信号量已减为0,进入等待,直到有其他任务完成,发送信号量+1 则,继续执行任务
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            dispatch_async(workConcurrentQueue, ^{
                NSLog(@"thread-info:%@开始执行任务%d",[NSThread currentThread],(int)i);
                sleep(1);
                NSLog(@"thread-info:%@结束执行任务%d",[NSThread currentThread],(int)i);
                //发送一个信号,信号总量会加1
                dispatch_semaphore_signal(semaphore);

            });
        });
    }
    NSLog(@"主线程...!");

执行效果如下:

第三题: 如何转换异步block为同步方式运行(异步转同步)?

解答:使用信号量dispatch_semaphore_t

  • 信号量相关API详解:
    dispatch_semaphore_create | 创建一个semaphore,可设置信号总量,注意要传入非负整数,否则创建的对象为nil,后续使用会crash
    dispatch_semaphore_signal | 发送一个信号(信号总量+1)
    dispatch_semaphore_wait | 等待信号(当信号总量为0时等待,信号量大于0时正常执行,且信号总量-1)
    信号量不会为负,因为当信号量等于0时,不再进行信号量减1操作
- (NSInteger)testAsyncToSync
{
    NSLog(@"testAsyncToSync 开始");
    __block NSInteger result = 0;
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    [self methodAsync:^(NSInteger value) {
        result = value;
        dispatch_semaphore_signal(sema);
    }];
    // 这里本来同步方法会立即返回,但信号量=0使得线程阻塞
    // 当异步方法回调之后,发送信号,信号量变为1,这里的阻塞将被解除,从而返回正确的结果,并且信号量变为0
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    NSLog(@"testAsyncToSync 结束 result:%ld", (long)result);
    return result;
}
// 异步方法
- (void)methodAsync:(void(^)(NSInteger result))callBack
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"methodAsync 异步开始");
        sleep(2);
        NSLog(@"methodAsync 异步结束");
        if (callBack) {
            callBack(5);
        }
    });
}

第四题:进行大文件读取,不允许将文件整个加载,并支持从指定位置开始读取N个字节,用什么方法可以实现?(dispatch_io_t )

- (void)testDispatchIO
{
    NSString *desktop = @"/Users/baidu/Desktop/TestGCD/TestGCD/";
    NSString *path = [desktop stringByAppendingPathComponent:@"AppDelegate.m"];

    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_fd_t fd = open(path.UTF8String, O_RDONLY);
    dispatch_io_t io = dispatch_io_create(DISPATCH_IO_RANDOM, fd, queue, ^(int error) {
        close(fd);
    });

    off_t beginSize = 200;//从文件200字节处开始读取
    size_t offset = 100;//每次读取字节数
    long long fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil].fileSize;//文件大小
    NSLog(@"文件大小为 %lld",fileSize);

    dispatch_group_t group = dispatch_group_create();
    NSMutableData *totalData = [NSMutableData data];

    dispatch_group_enter(group);
    dispatch_io_read(io, beginSize, offset, queue, ^(bool done, dispatch_data_t  _Nullable data, int error) {
        if (error == 0) {
            size_t len = dispatch_data_get_size(data);
            if (len > 0) {
                const void *bytes = NULL;
                (void)dispatch_data_create_map(data, (const void **)&bytes, &len);
                [totalData appendBytes:bytes length:len];
            }
        }
        if (done) {
            dispatch_group_leave(group);
        }
    });

    dispatch_group_notify(group, queue, ^{
        NSString *str = [[NSString alloc] initWithData:totalData encoding:NSUTF8StringEncoding];
        NSLog(@"读取的数据:  ---   %@", str);
    }); 
}
top Created with Sketch.