将来跳槽用面试题

iOS 线程相关面试题

2021-02-03  本文已影响0人  陈盼同学

问题:项目中用到的多线程技术


使用dispatch_once_t创建单例
使用dispatch_after延迟做事情
使用dispatch_async异步做一些耗时或者不影响整理流程的操作,比如清除缓存操作,异步网络请求


问题:六种组合


|----------------------------------------------------------------------- |
|           |   并发队列           |    手动创建的串行队列   |    主队列       |
|----------------------------------------------------------------------- |
|同步(sync)  |   没有开启新线程      |     没有开启新线程     |   没有开启新线程  |
|           |    串行执行任务       |     串行执行任务      |    串行执行任务   |
|----------------------------------------------------------------------- |
|异步(async)|  有开启新线程         |   有开启新线程        | "没有开启"新线程  |
|           |   并发执行任务        |     串行执行任务       |     串行执行任务 |
|-----------------------------------------------------------------------

问题:NSOperation


NSOperation的作用

配合使用NSOperation和NSOperationQueue也能实现多线程编程

NSOperation和NSOperationQueue实现多线程的具体步骤
先将需要执行的操作封装到一个NSOperation对象中
然后将NSOperation对象添加到NSOperationQueue中
系统会自动将NSOperationQueue中的NSOperation取出来
将取出的NSOperation封装的操作放到一条新线程中执行

NSOperation的子类

使用NSOperation子类的方式有3种
NSInvocationOperation
NSBlockOperation
自定义子类继承NSOperation,实现内部相应的方法

NSInvocationOperation

创建NSInvocationOperation对象

- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;

调用start方法开始执行操作

- (void)start;

一旦执行操作,就会调用target的sel方法

注意
默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作

- (void)viewDidLoad {
    [super viewDidLoad];
    [self invocationOperation];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)invocationOperation
{
    //以下两步只是在主线程执行run
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
    [op start];
}

- (void)run
{
    NSLog(@"------%@", [NSThread currentThread]);
}

@end

NSBlockOperation

创建NSBlockOperation对象

+ (id)blockOperationWithBlock:(void (^)(void))block;

通过addExecutionBlock:方法添加更多的操作

- (void)addExecutionBlock:(void (^)(void))block;

调用start方法开始执行操作

- (void)start;

eg:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self blockOperation];
}

- (void)blockOperation
{
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        // 在主线程
        NSLog(@"下载1------%@", [NSThread currentThread]);
    }];
    
    // 添加额外的任务(随机在主线程执行或者随机在子线程执行)
    [op addExecutionBlock:^{
        NSLog(@"下载2------%@", [NSThread currentThread]);
    }];

    [op addExecutionBlock:^{
        NSLog(@"下载3------%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"下载4------%@", [NSThread currentThread]);
    }];
    
    [op start];
}

NSOperationQueue

NSOperationQueue的作用
NSOperation可以调用start方法来执行任务,但默认是同步执行的
如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作

添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
最大并发数的相关方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
取消队列的所有操作
- (void)cancelAllOperations;
提示:也可以调用NSOperation的- (void)cancel方法取消单个操作
暂停和恢复队列
- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列。 队列暂停时(当前正在执行任务是不会停止)(将要调度到线程里任务)
(BOOL)isSuspended;(图片列表:加载+刷新)
操作优先级

设置NSOperation在queue中的优先级,可以改变操作的执行优先级

- (NSOperationQueuePriority)queuePriority;
- (void)setQueuePriority:(NSOperationQueuePriority)p;

优先级的取值
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
操作依赖

NSOperation之间可以设置依赖来保证执行顺序
比如一定要让操作A执行完后,才能执行操作B,可以这么写
[operationB addDependency:operationA]; // 操作B依赖于操作A

可以在不同queue的NSOperation之间创建依赖关系

操作监听

可以监听一个操作的执行完毕

自定义NSOperation

自定义NSOperation的步骤很简单
重写- (void)main方法,在里面实现想执行的任务

重写- (void)main方法的注意点
自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应

针对上面的自定义NSOperation和NSOperationQueue有如下的demo

首先自定义一个NSOperation,即新建一个类继承于NSOperation

.h

#import <Foundation/Foundation.h>

@interface XMGOperation : NSOperation

@end

.m

#import "XMGOperation.h"

@implementation XMGOperation

/**
 * 需要执行的任务  XMGOperation自定义的方法放在这里面,重写main
 */
- (void)main
{
    for (NSInteger i = 0; i<1000; i++) {
        //确保当前线程取消后能立即生效,视情况而定
        if (self.isCancelled) return;
        NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
    }
    
    //确保当前耗时线程取消后能生效,不执行download2,download3
    if (self.isCancelled) return;
    
    for (NSInteger i = 0; i<1000; i++) {
        NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]);
    }
    
    if (self.isCancelled) return;
    
    for (NSInteger i = 0; i<1000; i++) {
        NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]);
    }
    if (self.isCancelled) return;
    
}


@end
演示三种创建operation场景,并添加到NSOperationQueue中
- (void)operationQueue1
{
    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 创建操作(任务)
    // 创建NSInvocationOperation
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
    
    // 创建NSBlockOperation  ,这个依旧子线程
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3 --- %@", [NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"download4 --- %@", [NSThread currentThread]);
    }];
    [op3 addExecutionBlock:^{
        NSLog(@"download5 --- %@", [NSThread currentThread]);
    }];
    
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download6 --- %@", [NSThread currentThread]);
    }];
    
    // 创建自定义的operation对象 ,类名为XMGOperation,
    XMGOperation *op5 = [[XMGOperation alloc] init];
    
    // 添加任务到队列中
    [queue addOperation:op1]; // [op1 start] ,添加到队列中去,自动默认start
    [queue addOperation:op2]; // [op2 start]
    [queue addOperation:op3]; // [op3 start]
    [queue addOperation:op4]; // [op4 start]
    [queue addOperation:op5]; // [op5 start]
}

- (void)download1
{
    NSLog(@"download1 --- %@", [NSThread currentThread]);
}

- (void)download2
{
    NSLog(@"download2 --- %@", [NSThread currentThread]);
}
演示不用创建Blockoperation场景,直接给NSOperationQueue添加block
- (void)operationQueue2
{
    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
 
    
#warning 这两步可直接简化为给NSOperationQueue添加block
    // 创建操作
    //    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    //        NSLog(@"download1 --- %@", [NSThread currentThread]);
    //    }];
    
    // 添加操作到队列中
    //    [queue addOperation:op1];
    
    
    //上面两句作用 等同于 addOperationWithBlock
    
#warning 也就是下面这句 addOperationWithBlock
    [queue addOperationWithBlock:^{
        NSLog(@"download1 --- %@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download2 --- %@", [NSThread currentThread]);
    }];
}
演示NSOperationQueue并发数,设置为1直接就成串行了
- (void)opetationQueue3
{
    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 设置最大并发操作数
    //    queue.maxConcurrentOperationCount = 2;
    queue.maxConcurrentOperationCount = 1; // 就变成了子线程的串行队列,
    
    // 添加操作
    [queue addOperationWithBlock:^{
        NSLog(@"download1 --- %@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download2 --- %@", [NSThread currentThread]);
    }];
    
    [queue addOperationWithBlock:^{
        NSLog(@"download3 --- %@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download4 --- %@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download5 --- %@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download6 --- %@", [NSThread currentThread]);
    }];
}
演示取消和暂停
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 设置最大并发操作数
    queue.maxConcurrentOperationCount = 1; // 就变成了串行队列
    
    // 添加操作
    [queue addOperationWithBlock:^{
//        NSLog(@"download1 --- %@", [NSThread currentThread]);
//        [NSThread sleepForTimeInterval:1.0];
        for (NSInteger i = 0; i<5000; i++) {
            NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
        }
    }];
    [queue addOperationWithBlock:^{
//        NSLog(@"download2 --- %@", [NSThread currentThread]);
        //        [NSThread sleepForTimeInterval:1.0];
        for (NSInteger i = 0; i<1000; i++) {
            NSLog(@"download2 --- %@", [NSThread currentThread]);
        }
    }];
    [queue addOperationWithBlock:^{
//        NSLog(@"download3 --- %@", [NSThread currentThread]);
//        [NSThread sleepForTimeInterval:1.0];
        for (NSInteger i = 0; i<1000; i++) {
            NSLog(@"download3 --- %@", [NSThread currentThread]);
        }
    }];
    
    //添加自定义的Operation,在里面阻断外界暂停和取消操作
    [queue addOperation:[[XMGOperation alloc] init]];
    
    self.queue = queue;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//演示一 线程的暂停和继续
// 如果在线程里做耗时操作,执行到一半,暂停是暂时不会执行的,直到这个耗时线程做完会暂停
    
//    if (self.queue.isSuspended) {
//        // 恢复队列,继续执行
//        self.queue.suspended = NO;
//    } else {
//        // 暂停(挂起)队列,暂停执行
//        self.queue.suspended = YES;
//    }
 
//演示二 线程的取消
    //把线程取消,直接消亡
    [self.queue cancelAllOperations];
    
//eg:NSOperationQueue线程的暂停和取消功能,如果这个线程正在做一个耗时操作(比如遍历),暂停和取消会等到这个线程结束,才会结束queue,所以为了避免这种情况,自定义一个NSOperation(比如自己定义的XMGOperation),在耗时操作里监听取消和暂停状态,就是一个很好地将解决办法
}

演示线程依赖
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download1----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download2----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"download4----%@", [NSThread  currentThread]);
        }
    }];
    NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download5----%@", [NSThread  currentThread]);
    }];
    //op5线程完成以后调用completionBlock
    op5.completionBlock = ^{
        NSLog(@"op5执行完毕---%@", [NSThread currentThread]);
    };
    
    // 设置依赖 (再添加之前设置依赖)
    [op3 addDependency:op1];
    [op3 addDependency:op2];
    [op3 addDependency:op4];
    
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue addOperation:op5];
}
线程通信

NSThread线程通信

    // 回到主线程,显示图片
    //waitUntilDone为YES意味着当前方法会卡主,直到完成才会往下走
    [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];

    //    [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
    
    //    [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];

NSOperation的线程通信

    // 回到主线程
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        self.imageView.image = image;
    }];

GCD的线程通信

    // 回到主线程
    dispatch_async(dispatch_get_main_queue(), ^{
        self.imageView.image = image;
    });

GCD的一些函数


GCD的简单使用
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self syncConcurrent];
}

/**
 * 同步函数 + 主队列:(任务会卡住)
 */
- (void)syncMain
{
    NSLog(@"syncMain ----- begin");
    
    // 1.获得主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.将任务加入队列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
    
    NSLog(@"syncMain ----- end");
}

/**
 * 异步函数 + 主队列:只在主线程中执行任务 (这个特例说明了异步函数可以开启新线程,但并不是绝对可以开启新线程)
 */
- (void)asyncMain
{
    // 1.获得主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.将任务加入队列
    dispatch_async(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
}

/**
 * 同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务。任务是串行的,执 任务,再执行下一个任务
 */
- (void)syncSerial
{
    // 1.创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
    
    // 2.将任务加入队列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
}

/**
 * 异步函数 + 串行队列:会开启新的线程,但是任务是串行的,执行完一个任务,再执行下一个任务
 */
- (void)asyncSerial
{
    // 1.创建串行队列(串行队列只能创建)
    // DISPATCH_QUEUE_SERIAL 和 NULL 都会是串行队列
    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
    //  dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);
    
    // 2.将任务加入队列
    dispatch_async(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
}

/**
 * 同步函数 + 并发队列:不会开启新的线程(在主线程做)
 */
- (void)syncConcurrent
{
    // 1.获得全局的并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.将任务加入队列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
    NSLog(@"syncConcurrent--------end");
}

/**
 * 异步函数 + 并发队列:可以同时开启多条线程
 */
- (void)asyncConcurrent
{
    // 1.0创建一个并发队列
    // label : 相当于队列的名字  "com.520it.queue"
    // DISPATCH_QUEUE_CONCURRENT 队列类型,并发队列
    // dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
    
    // 1.1直接获得全局的并发队列
    //DISPATCH_QUEUE_PRIORITY_DEFAULT优先级,作用:优先执行顺序
    //默认传0
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.将任务加入队列
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"1-----%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"2-----%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"3-----%@", [NSThread currentThread]);
        }
    });
    
    NSLog(@"asyncConcurrent--------end");
//    dispatch_release(queue);  现在GCD的create现在不需要释放了
}

@end
队列组

在使用GCD进行任务操作时,有时会希望若干个任务执行之间有先后执行的依赖关系。

eg:

// 创建队列组
dispatch_group_t group = dispatch_group_create();
// 创建并发队列
dispatch_queue_t queue = dispatch_queue_create("my_queue", DISPATCH_QUEUE_CONCURRENT);

// 添加异步任务
dispatch_group_async(group, queue, ^{
    for (int i = 0; i < 5; i++) {
        NSLog(@"任务1-%@", [NSThread currentThread]);
    }
});

dispatch_group_async(group, queue, ^{
    for (int i = 0; i < 5; i++) {
        NSLog(@"任务2-%@", [NSThread currentThread]);
    }
});

//等前面的任务执行完毕后,会自动执行这个任务
dispatch_group_notify(group, queue, ^{
    dispatch_async(dispatch_get_main_queue(), ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务3-%@", [NSThread currentThread]);
        }
    });
});
/*下面这是大牛讲解时
//等前面的任务执行完毕后,会自动执行这个任务
dispatch_group_notify(group, dispatch_get_main_queue, ^{
    dispatch_async(dispatch_get_main_queue(), ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务3-%@", [NSThread currentThread]);
        }
    });
});
*/
dispatch_set_target_queue

用法一:指定优先级
?

用法二:控制队列执行阶层
如果在多个SERIAL Dispatch Queue中用dispatch_set_target_queue函数制定目标为某一个SERIAL Dispatch Queue,那么原本应并行执行的多个SERIAL Dispatch Queue,在目标SERIAL Dispatch Queue上只能同时执行一个处理。如下:

//1.创建目标队列
dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);

//2.创建3个串行队列
dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);

//3.将3个串行队列分别添加到目标队列
dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_set_target_queue(queue3, targetQueue);


dispatch_async(queue1, ^{
    NSLog(@"1 in");
    [NSThread sleepForTimeInterval:3.f];
    NSLog(@"1 out");
});

dispatch_async(queue2, ^{
    NSLog(@"2 in");
    [NSThread sleepForTimeInterval:2.f];
    NSLog(@"2 out");
});

dispatch_async(queue3, ^{
    NSLog(@"3 in");
    [NSThread sleepForTimeInterval:1.f];
    NSLog(@"3 out");
});

打印结果:
2019-05-06 15:52:36.025069+0800 Test[4273:498926] 1 in
2019-05-06 15:52:39.029506+0800 Test[4273:498926] 1 out
2019-05-06 15:52:39.029782+0800 Test[4273:498926] 2 in
2019-05-06 15:52:41.034457+0800 Test[4273:498926] 2 out
2019-05-06 15:52:41.034623+0800 Test[4273:498926] 3 in
2019-05-06 15:52:42.037019+0800 Test[4273:498926] 3 out

//如上,如果不设置targetQueue目标队列,那么queue1queue2queue3乱序跑。
dispatch_after

下面代码3秒之后执行,可以是任何线程

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);

dispatch_after(time, dispatch_get_main_queue(), ^{
    NSLog(@"waited at east three seconds.");
});
dispatch_group_notify
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{
    NSLog(@"1");
});
dispatch_group_async(group, queue, ^{
    NSLog(@"2");
});
dispatch_group_async(group, queue, ^{
    NSLog(@"3");
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"done");
});
dispatch_group_wait
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{
    NSLog(@"1");
});
dispatch_group_async(group, queue, ^{
    NSLog(@"2");
    sleep(1.0);
});
dispatch_group_async(group, queue, ^{
    NSLog(@"3");
});

//DISPATCH_TIME_FOREVER类型也是dispatch_time_t型
//注意点,一旦调用dispatch_group_wait函数,该函数就处于调用状态而不返回。即是当前线程已经卡在这儿,不向后执行,必须等待前面任务处理有结果或者自己设定的dispatch_time_t时间结束。
BOOL result =  dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
if (result == 0) {//返回值为0,那么全部处理执行结束
    NSLog(@"done");
}else{//如果时间过,但是前面的任务还没有跑完,会走到这儿
    NSLog(@"任务仍在执行");
};
dispatch_barrier_async
//栅栏的队列不能使全局的并发,要是手动创建的并发  (待考证)
dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"----3-----%@", [NSThread currentThread]);
});

//barrier栅栏效果
//官方解释:前面的执行完才到这,这个执行完才执行后面(★待考证)
dispatch_barrier_async(queue, ^{
    NSLog(@"----barrier-----%@", [NSThread currentThread]);
});

dispatch_async(queue, ^{
    NSLog(@"----4-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"----5-----%@", [NSThread currentThread]);
});
dispatch_apply

该函数按指定的次数将指定的block追加到指定的Dispatch Queue中,并等待全部处理结束。推荐在dispatch_async中非同步的执行dispatch_apply函数。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"----------------主线程开始------------");
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async( queue, ^{
        
        NSLog(@"子线程%@",[NSThread currentThread]);
        
        dispatch_apply(3, queue, ^(size_t index) {
            
            NSLog(@"追加第%zu次任务",index);
        });
        
        NSLog(@"done");
        
        //可以回到主线程进行通信了
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"刷新界面");
        });
    });
    
    NSLog(@"----------------主线程继续------------");
}

打印结果:
2019-05-07 10:16:06.001245+0800 Test[2820:134508] ----------------主线程开始------------
2019-05-07 10:16:06.001436+0800 Test[2820:134508] ----------------主线程继续------------
2019-05-07 10:16:06.001476+0800 Test[2820:134543] 子线程<NSThread: 0x600000c20a00>{number = 3, name = (null)}
2019-05-07 10:16:06.001567+0800 Test[2820:134543] 追加第0次任务
2019-05-07 10:16:06.001637+0800 Test[2820:134543] 追加第1次任务
2019-05-07 10:16:06.001700+0800 Test[2820:134543] 追加第2次任务
2019-05-07 10:16:06.001761+0800 Test[2820:134543] done
2019-05-07 10:16:06.009918+0800 Test[2820:134508] 刷新界面
dispatch_suspend和dispatch_resume

线程的挂起和恢复。dispatch_suspend并不会立即暂停正在运行的block,而是在当前block执行完成后,暂停后续的block执行。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    dispatch_queue_t queue = dispatch_queue_create("com.test.gcd", DISPATCH_QUEUE_SERIAL);
    //提交第一个block,延时2秒打印。
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"After 2 seconds...");
    });
    //提交第二个block,也是延时2秒打印
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"After 2 seconds again...");
    });
    //延时一秒
    NSLog(@"sleep 1 second...");
    sleep(1);
    //挂起队列
    NSLog(@"suspend...");
    dispatch_suspend(queue);
    //延时10秒
    NSLog(@"sleep 10 second...");
    sleep(10);
    //恢复队列
    NSLog(@"resume...");
    dispatch_resume(queue);
}

问题:GCD和nsopreation什么场景使用哪个好点


GCD主要特点:

1)GCG 是iOS4.0推出的,主要针对多核CPU 做了优化
2)GCD是 C 语言的技术
3)GCD 提供了一些 NSOperation 不具备的功能,比如一次性执行(创建单例),延迟执行,调度组.

NSOperation 特点:

  1. NSOperation 是 iOS2.0后推出的,iOS4.0之后重写了NSOperation.
  2. NSOperation 将操作(异步的任务)添加到队列(并发队列),就会执行制定操作的函数.
  3. NSOperation里可以方便的设置操作:
    1⃣️最大并发数
    2⃣️队列的暂停/继续
    3⃣️取消所有的操作
    4⃣️指定操作之间的依赖关系(GCD可以用同步实现)
    **使用NSOperation 需要注意几点点:
  4. 注意避免产生循环依赖
  5. 要先设置依赖关系,然后添加到队列

GCD 和 NSOperation的区别主要表现在以下几方面:

  1. GCD是一套 C 语言API,执行和操作简单高效,因此NSOperation底层也通过GCD实现,这是他们之间最本质的区别.因此如果希望自定义任务,建议使用NSOperation;

  2. 依赖关系,NSOperation可以设置操作之间的依赖(可以跨队列设置),GCD无法设置依赖关系,不过可以通过同步来实现这种效果;

  3. KVO(键值对观察),NSOperation容易判断操作当前的状态(是否执行,是否取消等),对此GCD无法通过KVO进行判断;
    (指的应该是:NSOperationQueue中使用了KVO,当NSOperation对象状态变化(finished,canceled等)时,观察者可以知道变化,从而做出相应的处理(移除已完成的等等))

  4. 优先级,NSOperation可以设置自身的优先级,但是优先级高的不一定先执行,GCD只能设置队列的优先级,如果要区分block任务的优先级,需要很复杂的代码才能实现;

  5. 继承,NSOperation是一个抽象类.实际开发中常用的是它的两个子类:NSInvocationOperation和NSBlockOperation,同样我们可以自定义NSOperation,GCD执行任务可以自由组装,没有继承那么高的代码复用度;

  6. 效率,直接使用GCD效率确实会更高效,NSOperation会多一点开销,但是通过NSOperation可以获得依赖,优先级,继承,键值对观察这些优势,相对于多的那么一点开销确实很划算,鱼和熊掌不可得兼,取舍在于开发者自己;

7)可以随时取消准备执行的任务(已经在执行的不能取消),GCD没法停止已经加入queue 的 block(虽然也能实现,但是需要很复杂的代码)

基于GCD简单高效,更强的执行能力,操作不太复杂的时候,优先选用GCD;而比较复杂的任务可以自己通过NSOperation实现.


问题:atomic


atomic用于保证属性setter、getter的原子性操作,相当于在setter和getter内部加了线程同步的锁,从而保证了setter和getter内部是线程同步的。

可以参考源码objc4的objc-accessors.mm
首先看设置属性

void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
{
    bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
    bool mutableCopy = (shouldCopy == MUTABLE_COPY);
    reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}

点击查看reallySetProperty方法

static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
    if (offset == 0) {
        object_setClass(self, newValue);
        return;
    }

    id oldValue;
    id *slot = (id*) ((char*)self + offset);

    if (copy) {
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        if (*slot == newValue) return;
        newValue = objc_retain(newValue);
    }

    if (!atomic) {
        oldValue = *slot;
        *slot = newValue;
    } else { //如果是atomic,可以看到加了一个spinlock_t自旋锁
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;
        slotlock.unlock();
    }

    objc_release(oldValue);
}

再看get属性

id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
    if (offset == 0) {
        return object_getClass(self);
    }

    // Retain release world
    id *slot = (id*) ((char*)self + offset);
    if (!atomic) return *slot;
        
    // Atomic retain release world
    spinlock_t& slotlock = PropertyLocks[slot]; //如果是atomic,可以看到加了一个spinlock_t自旋锁
    slotlock.lock();
    id value = objc_retain(*slot);
    slotlock.unlock();
    
    // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
    return objc_autoreleaseReturnValue(value);
}

它并不能保证"使用属性"的过程是线程安全的。只是保证了set和get方法内部是线程安全的,但是并不能保证属性的过程是线程安全的。

说atomic并不能保证使用属性的过程是线程安全的,

举个例子
1.对于NSArray类型 @property(atomic)NSArray *array我们用atomic修饰,数组的添加和删除并不是线程安全的,这是因为数组比较特殊,我们要分成两部分考虑,一部分是&array也就是这个数组本身,另一部分是他所指向的内存部分。atomic限制的只是&array部分,对于它指向的对象没有任何限制。
eg:

MJPerson *p = [[MJPerson alloc] init];//初始化类,类里面之前生命了一个可变数组dataArray属性
p.dataArray = [NSMutableArray array];//初始化dataArray,这里面p.dataArray是线程安全的,因为调用了set方法
[p.dataArray addObject:@"1"];  //这里面p.dataArray是线程安全的,因为调用了get方法,但是addObject却没有安全管控
[p.dataArray addObject:@"2"];

2、比如如果线程 A 调了 getter,与此同时线程 B 、线程 C 都调了 setter——那最后线程 A get 到的值,3种都有可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性的值,可能是 B set 的值,也有可能是 C set 的值。

如下

@property (atomic, assign) NSInteger intA;

- (void)viewDidLoad {
   [super viewDidLoad];
    
   //开启一个线程对intA的值+1
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       for (int i = 0;i < 1000;i ++){
           self.intA = self.intA + 1;
       }
       NSLog(@"首先加一:intA : %ld",(long)self.intA);
   });
   
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"首先加一后:intA : %ld",(long)self.intA);
    });
    
   //开启一个线程对intA的值+1
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       for (int i = 0;i < 1000;i ++){
           self.intA = self.intA + 1;
       }
       NSLog(@"其次加一:intA : %ld",(long)self.intA);
   });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       NSLog(@"其次加一后:intA : %ld",(long)self.intA);
    });
}
@end
打印:(因为都是异步,打印顺序并不固定)
2021-02-03 17:39:09.929929+0800 OC_Test[6945:206188] 首先加一:intA : 1000
2021-02-03 17:39:09.930341+0800 OC_Test[6945:206184] 其次加一:intA : 2000
2021-02-03 17:39:09.930385+0800 OC_Test[6945:206186] 其次加一后:intA : 2000
2021-02-03 17:39:09.931724+0800 OC_Test[6945:206189] 首先加一后:intA : 0

atomic:原子性
nonatomic:非原子性

声明属性时不写的话默认就是atomic

忙等

自旋锁(Spinlock)是一种忙等待锁,线程反复检查锁变量是否可用,不会挂起,避免了进程上下文的调度开销,适合阻塞很短时间的场合。当然也就不适合单CPU单线程上使用。


问题:dispatch_sync


意味着将指定的block“同步”追加到指定的Dispatch Queue中,在追加block结束之前,dispatch_sync函数会一直等待。可以认为是简单版的dispatch_group_wait

由于sync特性,所以使用sync函数往当前"串行"队列中添加任务,会卡住当前的串行队列(产生死锁)

死锁的原因是:队列引起的循环等待。由于当前串行队列执行到dispatch_sync处等待dispatch_sync的返回,dispatch_sync必须等待block执行完毕后才能返回,由于当前队列是串行队列,先进先出,但是我们通过dispatch_sync新放入的block位于队列后面,现在得不到执行,所以锁住了。

问题:以下代码是在主线程执行的,会不会产生死锁?会!
NSLog(@"执行任务1");

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
    NSLog(@"执行任务2");
});

NSLog(@"执行任务3");

原因:
因为把同步的任务2添加到主队列,dispatch_sync要求立马在当前线程同步执行任务,走到这一步想要立即执行。想要执行队列里的任务2,必须要等前面一整个任务执行完。任务3在等任务2,任务2在等任务3。
用图表示为

当前线程      |         主队列
任务1        |      viewDidLoad
sync        |       任务2
任务3        |

那么把上述的dispatch_sync换成dispatch_async会不会产生死锁?不会!
NSLog(@"执行任务1");

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
    NSLog(@"执行任务2");
});

NSLog(@"执行任务3");

原因:
dispatch_async不要求立马在当前线程同步执行任务
//打印结果为 执行任务1 执行任务3 执行任务2

问题:以下代码会不会产生死锁?不会!
问题:以下代码是在主线程执行的,会不会产生死锁?会!
NSLog(@"执行任务1");

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{ // 0
    NSLog(@"执行任务2");
    
    dispatch_sync(queue, ^{ // 1
        NSLog(@"执行任务3");
    });
    
    NSLog(@"执行任务4");
});

NSLog(@"执行任务5");
问题:以下代码是在主线程执行的,会不会产生死锁?会!
NSLog(@"执行任务1");

dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{ // 0
    NSLog(@"执行任务2");
    
    dispatch_sync(queue, ^{ // 1
        NSLog(@"执行任务3");
    });
    
    NSLog(@"执行任务4");
});

NSLog(@"执行任务5");
问题:以下代码是在主线程执行的,会不会产生死锁?不会!虽是同步,但确实并发队列
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"1");
    
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"2");
        dispatch_sync(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    
    NSLog(@"5");
    // Do any additional setup after loading the view.
}
下面代码执行会打印什么
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

dispatch_async(queue, ^{
    NSLog(@"1");
    [self performSelector:@selector(test) withObject:nil afterDelay:.0];
    NSLog(@"3");
    
    //添加这行代码,启动RunLoop程序才能打印2
    //[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

});
}
- (void)test
{
    NSLog(@"2");
}

答:会打印 1 3,但是2不会打印。因为performSelector:withObject:afterDelay:这句代码的本质是往Runloop中添加定时器,而子线程的Runloop在这里没有启动。
注意:- (id)performSelector:(SEL)aSelector withObject:(id)object;等不需要Runloop。

下面代码执行会打印什么
- (void)test
{
    NSLog(@"2");
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        NSLog(@"1");
        
        //启动RunLoop,保住线程。然后程序正常运行,打印了2
        //[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        //[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }];
    [thread start];
    
    [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
}

答:会打印 1 ,然后程序崩溃。因为执行完1,block结束后线程结束。


问题:信号量


dispatch_semaphore

semaphore叫做”信号量”
信号量的初始值,可以用来控制线程并发访问的最大数量
信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步

//定义一个控制最大量的值
int value= 1;
//初始化信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(value);
//执行到这句话时,如果信号量的值<=0,当前线程会进入睡眠等待(直到信号量的值>0);如果信号量的值>0,就减1,继续往下执行。
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
//让信号量的值加1
dispatch_semaphore_signal(semaphore);

信号量的使用场景

加锁
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = 0; i < 10000; i++) {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        //临界区,即待加锁的代码区域

        dispatch_semaphore_signal(semaphore);
    });
}

在进行多线程任务之前,首先创建一个计数为1的信号量,这样可以保证同一时刻只有一个线程在访问临界区。上面代码,系统会同时开启很多线程想要做遍历里面的事,但是同时只有一个线程能去做临界区的事情。

异步任务,同步返回
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
    __block NSArray *tasks = nil;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        //task赋值,代码有点长,就不贴了
        dispatch_semaphore_signal(semaphore);
    }];

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    return tasks;
}

上面是 AFNetworking 的一段代码,我且称之为异步去做,同步返回。这段代码的功能是通过异步的请求取得键路径为 keyPath 的任务数组 tasks,然后同步返回它。

控制线程并发数

在 GCD 中,dispatch_async() 异步操作可以产生新的线程,但是方法本身没办法限制线程的最大并发数,线程的创建和销毁是由 GCD 底层管理的。
了解 NSOperationQueue 的同学肯定知道,通过 maxConcurrentOperationCount 属性可以设置它的最大并发数。那么在GCD中,对应的解决方法就是使用信号量。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
for (int i = 0; i < 1000; ++i) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        //多线程代码
        
        dispatch_semaphore_signal(semaphore);
    });
}

其实跟加锁代码非常相似,区别在于,在初始化信号量时,将计数赋值为最大并发数。在应用场景上,限制线程并发数是为了性能考虑,而加锁是为了安全而考虑。

信号量和栅栏函数是锁吗

信号量是属于很多种加锁方式的一种,栅栏函数能实现读写锁的功能,所以应该都属于锁。


问题:线程池


线程池是一种线程使用模式。 线程过多会带来调度开销,进而影响缓存局部性和整体性能。 而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。 这避免了在处理短时间任务时创建与销毁线程的代价。

线程池的执行流程如图下:
1612430117402.jpg

若线程池大小小于核心线程池大小时
创建线程执行任务

若线程池大小大于等于核心线程池大小时
1.先判断线程池工作队列是否已满
2.若没满就将任务push进队列
3.若已满时,且maximumPoolSize>corePoolSize,将创建新的线程来4.执行任务
反之则交给饱和策略去处理

参数名代表意义

corePoolSize
线程池的基本大小(核心线程池大小)

maximumPool
线程池的最大大小

keepAliveTime
线程池中超过corePoolSize树木的空闲线程的最大存活时间

unit
keepAliveTime参数的时间单位

workQueue
任务阻塞队列

threadFactory
新建线程的工厂

handler
当提交的任务数超过maxmumPoolSize与workQueue之和时,任务会交给RejectedExecutionHandler来处理

上图的解释

我们知道,受限于硬件、内存和性能,我们不可能无限制的创建任意数量的线程,因为每一台机器允许的最大线程是一个有界值。也就是说ThreadPoolExecutor管理的线程数量是有界的。线程池就是用这些有限个数的线程,去执行提交的任务。然而对于多用户、高并发的应用来说,提交的任务数量非常巨大,一定会比允许的最大线程数多很多。为了解决这个问题,必须要引入排队机制,或者是在内存中,或者是在硬盘等容量很大的存储介质中。J.U.C提供的ThreadPoolExecutor只支持任务在内存中排队,通过BlockingQueue暂存还没有来得及执行的任务。

任务的管理是一件比较容易的事,复杂的是线程的管理,这会涉及线程数量、等待/唤醒、同步/锁、线程创建和死亡等问题。ThreadPoolExecutor与线程相关的几个成员变量是:keepAliveTime、allowCoreThreadTimeOut、poolSize、corePoolSize、maximumPoolSize,它们共同负责线程的创建和销毁。
corePoolSize:
线程池的基本大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。这里需要注意的是:在刚刚创建ThreadPoolExecutor的时候,线程并不会立即启动,而是要等到有任务提交时才会启动,除非调用了prestartCoreThread/prestartAllCoreThreads事先启动核心线程。再考虑到keepAliveTime和allowCoreThreadTimeOut超时参数的影响,所以没有任务需要执行的时候,线程池的大小不一定是corePoolSize。

maximumPoolSize:
线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。这里值得一提的是largestPoolSize,该变量记录了线程池在整个生命周期中曾经出现的最大线程个数。为什么说是曾经呢?因为线程池创建之后,可以调用setMaximumPoolSize()改变运行的最大线程的数目。

poolSize:
线程池中当前线程的数量,当该值为0的时候,意味着没有任何线程,线程池会终止;同一时刻,poolSize不会超过maximumPoolSize。

https://www.cnblogs.com/frankyou/p/10135212.html


问题:异步执行ABC之后在执行D的正确理解


首先异步执行ABC分两种情况,一种任务是同步的,一种任务是网络请求,发送操作是同步的,但是请求到的结果是异步的。

任务是同步时

第一种GCD group
dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_async(group, queue, ^{
        NSLog(@"同步任务A");
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"同步任务B");
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"同步任务C");
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"任务完成执行");
    });
第二种 dispatch_barrier_async
dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"任务A");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任务B");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任务C");
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"阻塞自定义并发队列");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任务D");
    });

    dispatch_async(queue, ^{
        NSLog(@"任务E");
     });
第三种 NSOperation
NSBlockOperation *operatioon1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务A");
    }];
    
    NSBlockOperation *operatioon2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务B");
    }];
    
    NSBlockOperation *operatioon3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务C");
    }];
    
    NSBlockOperation *operatioon4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任务D");
    }];
    
    [operatioon4 addDependency:operatioon1];
    [operatioon4 addDependency:operatioon2];
    [operatioon4 addDependency:operatioon3];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperations:@[operatioon1,operatioon2,operatioon3,operatioon4] waitUntilFinished:YES];
    NSLog(@"完成之后的操作");

任务时异步时(比如网络请求异步任务)

第一种 dispatch_group + semaphore
dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_group_async(group, queue, ^{
        NSLog(@"同步任务A");
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"网络异步任务一");
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    });
    
    dispatch_group_async(group, queue, ^{
        
        NSLog(@"同步任务B");
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"网络异步任务二");
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"同步任务C");
    });
    
    dispatch_group_async(group, queue, ^{
        
        NSLog(@"同步任务D");
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"网络异步任务四");
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"任务完成执行");
    });

第二种 dispatch_group_enter 和 dispatch_group_leave
dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        NSLog(@"同步任务A");
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"网络异步任务一");
            dispatch_group_leave(group);
        });
    });
    
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        
        NSLog(@"同步任务B");
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"网络异步任务二");
            dispatch_group_leave(group);
        });
    });
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        NSLog(@"同步任务C");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        
        NSLog(@"同步任务D");
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"网络异步任务四");
            dispatch_group_leave(group);
        });
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"任务完成执行");
    });
上一篇下一篇

猜你喜欢

热点阅读