jscjxysx 2019-07-01
线程是进程内假想的持有 cpu 使用权的执行单位,一个进程下可以创建多个线程并行执行;使用多线程的程序称为多线程运行,从程序开始执行是运行的程序成为主线程,除此之外之后生成的线程为次线程或子线程。
多个线程操作某个实例时,没有得到错误的结果或实例时,那么该类就称为线程安全。结果不能保证时,则称为非线程安全。
一般情况下,常数对象是线程安全的,变量对象不是线程安全的。
要想使用多线程不出错且高效执行,并行编程的知识必不可少,线程间的任务分配和信息交换、共享资源的互斥、与 GUI 的交互以及动画显示等,使用时都要格外小心。
NSThread 是苹果官方提供的面向对象操作线程技术,简单方便,可以直接操作对象,不过需要自己控制线程的生命周期,在平时较少使用。初始化创建 NSThread 的方法有如下几种:
/* 使用target对象的selector作为线程的任务执行体,该selector方法最多可以接收一个参数,该参数即为argument */ - (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); /* 使用block作为线程的任务执行体 */ - (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); /* 类方法,返回值为void 使用一个block作为线程的执行体,并直接启动线程 上面的实例方法返回NSThread对象需要手动调用start方法来启动线程执行任务 */ + (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); /* 类方法,返回值为void 使用target对象的selector作为线程的任务执行体,该selector方法最多接收一个参数,该参数即为argument 同样的,该方法创建完县城后会自动启动线程不需要手动触发 */ + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
// 获得主线程 + (NSThread *)mainThread; // 判断是否为主线程(对象方法) - (BOOL)isMainThread; // 判断是否为主线程(类方法) + (BOOL)isMainThread; // 获得当前线程 NSThread *current = [NSThread currentThread]; // 线程的名字——setter方法 - (void)setName:(NSString *)n; // 线程的名字——getter方法 - (NSString *)name; // 线程进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态 - (void)start; // 线程进入阻塞状态 + (void)sleepUntilDate:(NSDate *)date; + (void)sleepForTimeInterval:(NSTimeInterval)ti; // 线程进入死亡状态 + (void)exit;
// 在主线程上执行操作 - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray<NSString *> *)array; // equivalent to the first method with kCFRunLoopCommonModes // 在指定线程上执行操作 - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array NS_AVAILABLE(10_5, 2_0); - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0); // 在当前线程上执行操作,调用 NSObject 的 performSelector:相关方法 - (id)performSelector:(SEL)aSelector; - (id)performSelector:(SEL)aSelector withObject:(id)object; - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。在 Mac OS X 10.6 雪豹中首次推出,也可在 iOS 4 及以上版本使用。
1、创建队列(串行队列或并发队列)
2、将任务追加到队列中,系统根据任务类型执行任务(同步或者异步)
// 串行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
// 主队列的获取方法
dispatch_queue_t queue = dispatch_get_main_queue();
// 全局并发队列的获取方法
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);// 同步执行任务创建方法
dispatch_sync(queue, ^{
    // 这里放同步执行任务代码
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
    // 这里放异步执行任务代码
});我们可以看到,GCD 有两种创建任务的方法:同步或异步;三种队列:并发队列、串行队列和主队列,一共有六种的组合方式,我们逐个进行分析:
#pragma mark ------------------------GCD 基本使用(六种不同的组合)
#pragma mark -----------------------异步执行主队列:在主线程中串行执行任务
- (void)asyncMain {
    NSLog(@"mainThread---%@",[NSThread currentThread]); // 打印当前线程
    NSLog(@"asyncSerial---begin");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        for(int i=0;i<2;i++){//        任务1
            [NSThread sleepForTimeInterval:2];//模拟耗时操作
            NSLog(@"1-------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        //        任务2
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2-------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        //        任务3
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3-------%@",[NSThread currentThread]);
        }
    });
    NSLog(@"main---end");
}
#pragma mark -----------------------同步执行主队列
//在主线程中使用 同步执行主队列,程序会出现死锁
- (void)syncMain {
    NSLog(@"mainThread---%@",[NSThread currentThread]); // 打印当前线程
    NSLog(@"syncSerial---begin");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        for(int i=0;i<2;i++){//        任务1
            [NSThread sleepForTimeInterval:2];//模拟耗时操作
            NSLog(@"1-------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        //        任务2
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2-------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        //        任务3
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3-------%@",[NSThread currentThread]);
        }
    });
    NSLog(@"main---end");
}
#pragma mark -----------------------异步执行串行对类:开启新线程,在当前线程下串行执行任务,任务不做等待
- (void)asyncSerial {
    NSLog(@"serialThread---%@",[NSThread currentThread]); // 打印当前线程
    NSLog(@"asyncSerial---begin");
    dispatch_queue_t queue = dispatch_queue_create("serial.queue.test", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        //        任务1
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];//模拟耗时操作
            NSLog(@"1-------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        //        任务2
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2-------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        //        任务3
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3-------%@",[NSThread currentThread]);
        }
    });
    NSLog(@"asyncSerial---end");
}
#pragma mark -----------------------同步执行串行对类:不开启新线程,在当前线程下串行执行任务
- (void)syncSerial {
    NSLog(@"serialThread---%@",[NSThread currentThread]); // 打印当前线程
    NSLog(@"syncSerial---begin");
    dispatch_queue_t queue = dispatch_queue_create("serial.queue.test", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        //        任务1
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];//模拟耗时操作
            NSLog(@"1-------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        //        任务2
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2-------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        //        任务3
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3-------%@",[NSThread currentThread]);
        }
    });
    NSLog(@"syncSerial---end");
}
#pragma mark -------------------------异步执行并发队列:开启多个线程,任务交替(同时)执行
- (void)asyncConcurrent {
    NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
    NSLog(@"asyncConcurrent---begin");
    dispatch_queue_t queue = dispatch_queue_create("concurrent.queue.test", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        //        任务1
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];//模拟耗时操作
            NSLog(@"1-------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        //        任务2
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2-------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        //        任务3
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3-------%@",[NSThread currentThread]);
        }
    });
    NSLog(@"asyncConcurrent---end");
}
#pragma mark ------------------------同步执行并发队列:不开启新线程,执行完一个任务在执行下一个任务,因为只有一个线程
- (void)syncConcurrent {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"syncConcurrent---begin");
    dispatch_queue_t queue = dispatch_queue_create("concurrent.queue.test", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
//        任务1
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];//模拟耗时操作
            NSLog(@"1-------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        //        任务2
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2-------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        //        任务3
        for(int i=0;i<2;i++){
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3-------%@",[NSThread currentThread]);
        }
    });
    NSLog(@"syncConcurrent---end");
}/**
 * 线程间通信
 */
- (void)communication {
    // 获取全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    // 获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue(); 
    
    dispatch_async(queue, ^{
        // 异步追加任务
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
        
        // 回到主线程
        dispatch_async(mainQueue, ^{
            // 追加在主线程中执行的任务
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        });
    });
}我们有时需要异步执行两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于栅栏一样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到dispatch_barrier_async方法在两个操作组间形成栅栏。
- (void)barrier {
    dispatch_queue_t queue = dispatch_queue_create("concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_barrier_async(queue, ^{
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"barrier---%@",[NSThread currentThread]);      // 打印当前线程
    });
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务4
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"4---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
}/**
 * 延时执行方法 dispatch_after
 */
- (void)after {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"asyncMain---begin");
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2.0秒后异步追加任务代码到主队列,并开始执行
        NSLog(@"after---%@",[NSThread currentThread]);  // 打印当前线程
    });
}/**
 * 一次性代码(只执行一次)dispatch_once
 */
- (void)once {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 只执行1次的代码(这里面默认是线程安全的)
    });
}监听 group 中任务的完成状态,当所有的任务都执行完成后,追加任务到 group 中,并执行任务。
/**
 * 队列组 dispatch_group_notify
 */
- (void)groupNotify {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"group---begin");
    
    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
        NSLog(@"group---end");
    });
}暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行。
队列组 dispatch_group_wait
-(void)groupWait {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"group---begin");
    
    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    // 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    NSLog(@"group---end");
}- (void)groupEnterAndLeave
{
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"group---begin");
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的异步操作都执行完毕后,回到主线程.
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
        NSLog(@"group---end");
    });
    
    //    // 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)
    //    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    //
    //    NSLog(@"group---end");
}使用Dispatch Semaphore 可以实现线程同步,将异步执行任务转换为同步执行任务。
- (void)semaphoreSync {
    
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"semaphore---begin");
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    __block int number = 0;
    dispatch_async(queue, ^{
        // 追加任务1
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        
        number = 100;
        
        dispatch_semaphore_signal(semaphore);
    });
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"semaphore---end,number = %zd",number);
}NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。
NSOperation 需要配合 NSOperationQueue 来实现多线程。因为默认情况下,NSOperation 单独使用时系统同步执行操作,配合 NSOperationQueue 我们能更好的实现异步执行。
NSOperation 实现多线程的使用步骤分为三步:
NSOperation 是个抽象类,不能用来封装操作。我们只有使用它的子类来封装操作。我们有三种方式来封装操作。
使用子类 NSInvocationOperation
使用子类 NSBlockOperation
自定义继承自 NSOperation 的子类,通过实现内部相应的方法来封装操作。
/**
 * 使用子类 NSInvocationOperation
 */
- (void)useInvocationOperation {
    // 1.创建 NSInvocationOperation 对象
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
    // 2.调用 start 方法开始执行操作
    [op start];
}
/**
 * 任务1
 */
- (void)task1 {
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
    }
}/**
 * 使用子类 NSBlockOperation
 */
- (void)useBlockOperation {
    // 1.创建 NSBlockOperation 对象
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    // 2.调用 start 方法开始执行操作
    [op start];
}NSBlockOperation 还提供了一个方法 addExecutionBlock:通过 addExecutionBlock: 就可以为 NSBlockOperation 添加额外的操作。
NSOperationQueue 一共有两种队列:主队列、自定义队列。其中自定义队列同时包含了串行、并发功能。下边是主队列、自定义队列的基本创建方法和特点。
// 主队列获取方法 队列中代码在主线程运行 NSOperationQueue *queue = [NSOperationQueue mainQueue]; // 自定义队列创建方法 队列中代码在子线程运行 NSOperationQueue *queue = [[NSOperationQueue alloc] init];
/**
 * 使用 addOperation: 将操作加入到操作队列中
 */
- (void)addOperationToQueue {
    // 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 2.创建操作
    // 使用 NSInvocationOperation 创建操作1
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
    // 使用 NSInvocationOperation 创建操作2
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];
    // 使用 NSBlockOperation 创建操作3
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"3---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [op3 addExecutionBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"4---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    // 3.使用 addOperation: 添加所有操作到队列中
    [queue addOperation:op1]; // [op1 start]
    [queue addOperation:op2]; // [op2 start]
    [queue addOperation:op3]; // [op3 start]
}最大并发操作数:maxConcurrentOperationCount
/**
 * 设置 MaxConcurrentOperationCount(最大并发操作数)
 */
- (void)setMaxConcurrentOperationCount {
    // 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 2.设置最大并发操作数
    queue.maxConcurrentOperationCount = 1; // 串行队列
// queue.maxConcurrentOperationCount = 2; // 并发队列
// queue.maxConcurrentOperationCount = 8; // 并发队列
    // 3.添加操作
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"3---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"4---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
}NSOperation、NSOperationQueue 最吸引人的地方是它能添加操作之间的依赖关系。通过操作依赖,我们可以很方便的控制操作之间的执行先后顺序
例:比如说有 A、B 两个操作,其中 A 执行完操作,B 才能执行操作。
- (void)addDependency {
    // 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 2.创建操作
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];
    // 3.添加依赖
    [op2 addDependency:op1]; // 让op2 依赖于 op1,则先执行op1,在执行op2
    // 4.添加操作到队列中
    [queue addOperation:op1];
    [queue addOperation:op2];- (void)communication {
    // 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    // 2.添加操作
    [queue addOperationWithBlock:^{
        // 异步进行耗时操作
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
        }
        // 回到主线程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            // 进行一些 UI 刷新等操作
            for (int i = 0; i < 2; i++) {
                [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
                NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
            }
        }];
    }];
}把这一块单独提出来是因为无论是使用 NSThread、GCD、NSOperation 等,在多个地方异步同时调用同一方法,会造成结果不符合预期,也就是线程不安全。
线程不安全解决方案:可以给线程加锁,在一个线程执行该操作的时候,不允许其他线程进行操作。iOS 实现线程加锁有很多种方式。@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock、atomic(property) 等等各种方式,@synchronized、 NSLock这两种方式比较常用。
银行取钱案例(例子是使用NSThread开辟的线程,给执行的代码加锁(或同步代码块),其他两种多线程方式同理)
- (void)getMoney {
    Account *account = [[Account alloc] init];
    account.accountNumber = @"1603121434";
    account.balance = 1500.0;
    
    NSThread *thread1 = [[NSThread alloc] initWithTarget:account selector:@selector(draw:) object:@(1000)];
    [thread1 setName:@"Thread1"];
    
    NSThread *thread2 = [[NSThread alloc] initWithTarget:account selector:@selector(draw:) object:@(1000)];
    [thread2 setName:@"Thread2"];
    
    [thread1 start];
    [thread2 start];
}
- (void)draw:(id)money
{
//    当多个线程同时操作的时候,会存在竞争条件,数据结果就无法保证
//    double drawMoney = [money doubleValue];
//    //判断余额是否足够
//    if (self.balance >= drawMoney)
//    {
//        //当前线程睡1毫秒
//        [NSThread sleepForTimeInterval:0.001];
//        self.balance -= drawMoney;
//        NSLog(@"%@ draw money %lf balance left %lf", [[NSThread currentThread] name], drawMoney, self.balance);
//    }else{
//        //余额不足,提示
//        NSLog(@"%@ Balance Not Enouth", [[NSThread currentThread] name]);
//    }
    
    
//    我们对draw:方法添加了一个同步代码块,使用@synchronized包围的代码即为同步代码块,同步代码块需要一个监听器,我们使用account对象本身作为监听器,因为是account对象产生的竞争条件,当执行同步代码块时需要先获取监听器,如果获取不到则线程会被阻塞,当同步代码块执行完成则释放监听器
//    @synchronized (self) {
//        double drawMoney = [money doubleValue];
//        if (self.balance >= drawMoney)
//        {
//            //当前线程睡1毫秒
//            [NSThread sleepForTimeInterval:1];
//            self.balance -= drawMoney;
//            NSLog(@"%@ draw money %lf balance left %lf", [[NSThread currentThread] name], drawMoney, self.balance);
//        }else{
//            //余额不足,提示
//            NSLog(@"%@ Balance Not Enouth", [[NSThread currentThread] name]);
//        }
//    }
    
    
    
//    我们使用锁机制,创建了一个NSLock类的锁对象,lock方法用于获取锁,如果锁被其他对象占用则线程被阻塞,unlock方法用于释放锁,以便其他线程加锁。
    [self.lock lock];
    double drawMoney = [money doubleValue];
    if (self.balance >= drawMoney)
    {
        //当前线程睡1毫秒
        [NSThread sleepForTimeInterval:1];
        self.balance -= drawMoney;
        NSLog(@"%@ draw money %lf balance left %lf", [[NSThread currentThread] name], drawMoney, self.balance);
    }else{
        //余额不足,提示
        NSLog(@"%@ Balance Not Enouth", [[NSThread currentThread] name]);
    }
    [self.lock unlock];
}本文所涉及的代码:threads